顺序表和链表的区别、链表的OJ题

顺序表和链表的区别

顺序表的优缺点

优点

  • 空间连续
  • 支持随机访问

缺点

  • 顺序表中间/头部的插入删除,时间复杂度为O(N)
  • 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  • 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

链表的优缺点

优点

  • 任意位置插入删除时间复杂度为O(1)
  • 没有增容问题,插入一个开辟一个空间。

缺点

  • 以节点为单位存储,不支持随机访问

区别

顺序表存储位置是相邻连续的,可以随即访问的一种数据结构,一个顺序表在使用前必须指定起长度,一旦分配内存,则在使用中不可以动态的更改。他的优点是访问数据是比较方便,可以随即的访问表中的任何一个数据。

链表是通过指针来描述元素关系的一种数据结构,他可以是物理地址不连续的物理空间。不能随即访问链表元素,必须从表头开始,一步一步搜索元素。它的优点是:对于数组,可以动态的改变数据的长度,分配物理空间。

在使用中:如果一个数组在使用中,查询比较多,而插入,删除数据比较少,数组的长度不变时,选顺序表比较合理。如果插入,删除,长度不定的数组,可以选链表。

链表的OJ题

1. 删除链表中等于给定值 val 的所有节点。 OJ链接

先判断头节点是否为空,如果为空,直接返回。

定义当前节点为 cur,当前节点的前一个节点为 prve。
如果当前节点的值 cur.val 等于 val,就让 prve的next指向cur的next,cur往前走;反之,cur和prve都往前走。
当cur为空时退出循环,再去判断头节点的值是否等于val。
最后返回头节点。

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){
            return null;
        }
        ListNode prve = head;
        ListNode cur = prve.next;
        while(cur != null){
            if(cur.val == val){
                prve.next = cur.next;
                cur = cur.next;
            }else{
                prve = prve.next;
                cur = cur.next;
            }
        }
        if(head.val == val){
            head = head.next;
        }
        return head;
    }
}

2. 反转一个单链表。 OJ链接

如果头节点为空,直接返回Null。

定义一个新的头节点 bs,尾节点 be;定义当前节点为 cur 其前一个节点为 prve;

如果 bs 为空,则令 bs = cur,be = cur;并令 be 的 next 为空
如果不为空,则用单链表的头节点插入法依次插入 cur ,最后返回新的头节点 bs

(注意循环条件是 cur.next != null ,则在退出循环之后应将当前 cur 插入 bs中去)

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode bs = null;
        ListNode be = null;

        ListNode cur = head;
        ListNode prve = head.next;
        while(cur.next != null){
            if(bs == null){
                bs = cur;
                be = cur;
                be.next = null;
            }else{
                cur.next = bs;
                bs = cur;
            }
            cur = prve;
            prve = prve.next;
        }
        if(cur.next == null){
                cur.next = bs;
                bs = cur;
        }
        return bs;
    }
}

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。OJ链接

给定两个节点 slow 和 fast,slow每次走一个节点,fast每次走两个节点,当 fast 为空或者是 fast.next 为空,slow就到达中心节。

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null  && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

4. 输入一个链表,输出该链表中倒数第k个结点。OJ链接

1、先判断头节点是否为空
2、得到这个链表的长度 count
3、判断给出的 k 值是否合法
4、从头节点开始出发,判断 count 是否等于 k ,不等于就让 count–

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head == null){
            return null;
        }
        ListNode cur = head;
        int count= 0;
        while(cur != null){
            count++;
            cur = cur.next;
        }
        if(k < 0 || k > count ){
            return null;
        }
        cur = head;
        while(cur != null && count != k ){
            cur = cur.next;
            count--;
        }
        if(cur == null){
            return null;
        }
        return cur;
    }
}

5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。[OJ链接]

1、先判断这两个有序链表是否为空
2、定义一个新的链表的头节点为 head ,当前节点为 cur ,然后去遍历这两个有序链表
3、当这两个有序链表都不为空时进入循环
4、退出循环后要判断 l1 或 l2是否不为空,若不为空就将 l1 或 l2 剩下的所有元素并入新链表中

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1 == null){
            return l2;
        }
        if(l2 == null){
            return l1;
        }
        ListNode head=null;
        ListNode cur = null;
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                if(head == null){
                    head = l1;
                    cur = l1;
                    l1 = l1.next;
                }else{
                    cur.next = l1;
                    cur = l1;
                    l1 = l1.next;
                }
            }else{
                if(head == null){
                    head = l2;
                    cur = l2;
                    l2 = l2.next;
                }else{
                    cur.next = l2;
                    cur = l2;
                    l2 = l2.next;
                }
            }
    }
    if(l1 != null){
        cur.next = l1;
    }
    if(l2 != null){
        cur.next = l2;
    }
    return head;
    }
}

6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。[OJ链接]

1、定义两个链表

链表 头节点 尾节点
1 小于 x 的链表 as ae
2 大于等于 x 的链表 bs be

2、遍历给定的链表,将小于 x 的以尾插法的形式放入1 中,将其他放入 2 中
3、完成之后判断两个链表是否为空,若其中一个为空则返回 pHead,若都不为空,则将第一个链表的尾节点指向第二个链表的头节点,并将第二个链表的尾节点的 next 指为 null

import java.util.*;

public class Partition {
public ListNode partition(ListNode pHead, int x) {
    ListNode bs = null;
    ListNode be = null;
    
    ListNode as = null;
    ListNode ae = null;
    
    ListNode cur = pHead;
    
    while(cur != null){
        if(cur.val < x ){
            if(as == null){
                as = cur;
                ae = cur;
            }else{
                ae.next = cur;
                ae = ae.next;
            }
        }else{
            if(bs == null){
                bs = cur;
                be = cur;
            }else{
                be.next = cur;
                be = be.next;
            }
        }
        cur= cur.next;
    }
    if(as == null || bs == null){
        return pHead;
    }
    if(as != null && bs != null){
        ae.next = bs;
        be.next = null;
    }
    return as;
	}
}

7. 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 OJ链接

public class Solution {
    public ListNode deleteDuplication(ListNode pHead){
        ListNode cur=pHead;
        ListNode newHead=new ListNode(-1);
        ListNode temp=newHead;
        while (cur != null) {
            if (cur.next != null && cur.val == cur.next.val) {
                while (cur.next != null && cur.val == cur.next.val ) {
                    cur=cur.next;
                }
                cur=cur.next;
            }else {
                temp.next = cur;
                temp=cur;
                cur=cur.next;
            }
        }
        temp.next=null;
        return newHead.next;
    }
}

8. 链表的回文结构。OJ链接

1、首先找到链表的中间节点,用 slow 和 fast 方法去找
2、将 slow 之后的这部分链表进行反转,反转之后的头节点记为 sl
3、记当前节点为 cur ,判断 cur 的值和 sl 的值是否相等,若不相等,则直接返回 false,否则就让 cur 和sl 指向其下一个节点,直到 cur 的 next 等于 slow 退出循环
4、因为退出循环时的 当前 cur 和 sl 的值没有进行比较,所以在用 if 语句判断一下,如果相等,返回 true,否则返回 false。

public class PalindromeList {
    public boolean chkPalindrome(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        
        ListNode sl = slow;
        ListNode tmp = slow.next;
        while(tmp != null ){
            ListNode prve = tmp.next;
            tmp.next = sl;
            sl = tmp;
            tmp = prve;
        }
        ListNode cur = head;
        while(cur.next != slow){
            if(cur.val != sl.val){
                return false;
            }
            cur = cur.next;
            sl = sl.next;
        }
        if(cur.val == sl.val){
            return true;
        }
        return false;
    }
}

9. 输入两个链表,找出它们的第一个公共结点。OJ链接

1、首先判断给定的两个链表是否为空,若其中任意一个为空,就直接返回 null
2、若都不为空,则求得两个链表的长度,这里将 headA 的长度记为 lenA, headB 的长度记为 lenB
3、记两个链表长度的差值为 len = lenA - lenB
4、如果 len < 0 ,则说明 lenB > lenA ,令 len = lenB - lenA,对B这个链表走 len 个节点
5、如果 len >= 0 ,则说明 lenA > lenB,对A这个链表走 len 个节点
6、然后再让两个链表一起走
7、当走完任意一个链表,就返回 null,否则就返回当前节点

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 
        if(headA == null || headB == null){
            return null;
        }
        ListNode curA = headA;
        ListNode curB = headB;

        int lenA = 0;
        int lenB = 0;

        while(curA != null){
            lenA++;
            curA = curA.next;
        }
        while(curB != null){
            lenB++;
            curB = curB.next;
        }
        int len = lenA - lenB;
        curA = headA;
        curB = headB;

        if(len < 0){
            len = lenB - lenA;
            lenB = 0;
            while(len != lenB){
                curB = curB .next;
                lenB++;
            }
        }else{
            lenA = 0;
            while(len != lenA){
                curA = curA.next;
                lenA++;
            }
        }
        while(curA != curB && curB != null && curA != null){
            curA = curA.next;
            curB = curB.next;
        }
        if(curB == null || curA == null){
            return null;
        }
        return curA;
    }
}

10. 给定一个链表,判断链表中是否有环。 OJ链接

1、用fast和slow方法
2、如果链表中有环,则肯定会出现fast = slow
(注意这里用的是fast是slow的二倍,也可以是三倍,但是三倍会有跳跃,可能会出现用时较久)

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        if(fast == null || fast.next == null){
            return false;
        }
        return true;
    }
}

11. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null 。 OJ链接

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast= fast.next.next;
            slow = slow.next;
            if(slow == fast){
                break;
            }
        }
        if(fast == null || fast.next == null){
            return null;
        }
        slow = head;
        while(slow != fast){
            slow =slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

你可能感兴趣的:(Java,链表,链表,java,算法,单链表)