单链表典型问题总结

1、 删除链表中等于给定值 **val** 的所有节点。 

思路分析:

定义两个辅助指针 prev 和 cur,当prev.next.data= val时删除即可

主要是别忘了处理第一个节点

 public void removeAllKey(int key) {
        ListNode prev = this.head;
        ListNode cur = this.head.next;
        while(cur != null) {
            if (prev.next.data == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        //处理第一个节点--最后处理
        if (this.head.data == key) {
            this.head = this.head.next;
        }
    }
2. 反转一个单链表。

 
①定义辅助指针cur、prev、curNext。让cur往后走,cur.next = prev
                                                                                    prev = cur
                                                                                    cur = curNext
②走到最后会发现curNext为空时,cur所指向的节点就是新的头。
 
注意:
a. 要把原来头节点的next域置为空
 
 
 public ListNode reverseList() {
        if(this.head == null) {
            return null;
        }
        ListNode cur = this.head;
        ListNode prev = null;
        ListNode newHead = null;
        while (cur != null) {
            ListNode curNext = cur.next;
            if (curNext == null) {
                newHead = cur;
            }
            cur.next = prev;
            prev = cur;
            cur = curNext;
        }
        return newHead;
    }
3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
 
思路分析:快慢指针法。(只遍历链表一遍)
 
   public ListNode middleNode() {
//        ListNode cur = this.head;
//        int length = getlength()/2;
//        for (int i = 0; i < length; i++) {
//            cur = cur.next;
//        }
//        return cur;
        ListNode fast = this.head;
        ListNode slow = this.head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
4. 输入一个链表,输出该链表中倒数第 k 个结点。
 
思路分析:
让快指针走k-1步,然后让快指针和慢指针同时走。快指针走到最后时,慢指针所在的节点就是中间节点。

注意:首先要判断k是否有效,最高效的办法就是最多走节点个数-1步,因此在快指针走的同时加以判断,fast.next = null;说明k不合法返回null即可。

 
 public ListNode FindKthToTail(int k) {
        if (k <= 0 || this.head == null) {
            return null;
        }
        ListNode fast = this.head;
        ListNode slow = this.head;
        for (int i = 0; i < k-1; i++) {
            if (fast.next == null) {
                return null;
            }
            fast = fast.next;
        }
        while(fast.next != null) {
            fast = fast.next;
            slow = slow.next;

        }
        return slow;
    }
5. 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
 
思路分析:
若要合并的有序链表时headA和headB。创建一个虚拟的节点newHead,将headA和headB节点的数据进行比较,把较小的数据加入newHead为头节点的链表中。
 
注意:
要判断如果一个链表为空,直接把另一个链表加入
 
 public static ListNode mergeTwoLists1(ListNode headA, ListNode headB){
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;
        while (headA != null && headB != null) {
            if (headA.data < headB.data) {
                tmp.next = headA;
                headA = headA.next;
                tmp = tmp.next;
            } else {
                tmp.next = headB;
                headB = headB.next;
                tmp = tmp.next;
            }
        }
       //如果a为空
        if (headA == null) {
            tmp.next = headB;
        } else {
            tmp.next = headA;
        }
        return newHead.next;
    }

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

思路分析:
①首先,要有两个引用 beginStart和beginEnd 、afterStart和afterEnd
②当链表中节点的数据比k小时,放入beginStart指向的这个节点,当链表中节点的数据比k大时,放入afterStart指向的这个节点。
放的时候要注意是否为第一次放。
③将beginEnd和afterStart连起来 

看起来好像结束了,其实漏掉了两个问题:
a.如果链表中的元素全部都小于k,那么返回bs完全没有问题。。但是如果链表中的元素全都大于k,说明bs为空,那么直接返回as即可,加一个判断。
b.当我们返回bs的时候,会发现打印链表终止条件是null,但是放完链表时,ae.next不一定为空,因此最后我们要把ae.next置为空。  这样的话才可行嘿嘿~

  

 public ListNode partition(int x){
        ListNode bs = null;
        ListNode be = null;
        ListNode as = null;
        ListNode ae = null;
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.data < x) {
                //判断是否为第一次加入
                if (bs == null) {
                    bs = cur;
                    be = cur;
                } else {
                    be.next = cur;
                    be = be.next;
                }
            } else {
                //判断是否为第一次加入
                if (as == null) {
                    as = cur;
                    ae = cur;
                } else {
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
       //连在一起
        if (be.next != null) {
            be.next = as;
        }
        //判断是否k是否小于所以节点
        if (bs == null) {
            return as;
        }
        if (as != null) {
            ae.next = null;
        }
        return bs;
}

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

思路分析:
①寻找重复节点,定义一个辅助变量cur遍历单链表,如果cur.data = cur.next.data时说明有节点数据重复。但是要注意重复的节点不止一个,因此判断时要用循环。
②删除重复的节点

注意:
a.每次判断判断cur.data == cur.next.data时都要判断cur.next 是否为 null。否则会造成空指针异常。
b.如果最后一个节点也为重复节点,那么要 tmp.next = null;

  public ListNode deleteDuplication(){
        ListNode cur = this.head;
        ListNode newHead = new ListNode(-1);
        ListNode tmp = newHead;
        while (cur != null) {
            //重复的节点
            if(cur.next != null && cur.data == cur.next.data) {
                //每一次都需要判断cur.next
                while (cur.next != null && cur.data == cur.next.data) {
                    cur = cur.next;
                }
                cur = cur.next;
            } else {
                tmp.next = cur;
                tmp = tmp.next;
                cur = cur.next;
            }
        }
        //最后一个节点如果也是重复的,需要将tmp.next置为空
        tmp.next = null;
        return newHead.next;
    }

 8. 链表的回文结构。

回文就是倒着数和正着数得出来的数字序列相同。如12321、 1221
如何判断链表的回文结构呢?
 
思路分析:
①找到链表的中间节点
②反转链表
③fast/slow往前    head往后走
 
注意:
a.要判断为偶数个数字的情况
b.反转链表时要注意fast和fast.next为空的情况
 
 
    //单链表判断回文数
    public boolean chkPalindrome() {
        //为空
        if (this.head == null) {
            return false;
        }
        //一个数
        if (head.next == null) {
            return true;
        }
        //1、找到单链表的中间节点
        ListNode fast = this.head;
        ListNode slow = this.head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //2、反转单链表
        ListNode cur = slow.next;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        //3、fast/slow往前    head往后走
        while (this.head != slow) {
            if (this.head.data != slow.data) {
                return false;
            }
            //偶数个数
            if (this.head.next == slow) {
                return true;
            }
            this.head = this.head.next;
            slow = slow.next;
        }
        return true;
    }

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

思路分析:
①将pL指向长链表,pL指向短的链表
②让长链表先走差值步
③分别一步一步走,如果节点相同就为相交

注意:
注意计算完长度后pS、pL重新赋值,还有最后循环退出的条件

    public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) {
            return null;
        }
        ListNode pL = headA;//永远指向长的单链表
        ListNode pS = headB;//永远指向短的单链表
        int lenA = 0;
        int lenB = 0;
        //求的lenA  lenB
        while (pL != null) {
            lenA++;
            pL = pL.next;
        }
        while (pS != null) {
            lenB++;
            pS = pS.next;
        }
        //pl、ps为空了
        pL = headA;
        pS = headB;
        //差值-》最长的单链表先走len步
        int len = lenA-lenB;
        if(len < 0) {
            pL = headB;
            pS = headA;
            len = lenB-lenA;
        }
        //让pL先走len步
        for (int i = 0; i < len; i++) {
            pL = pL.next;
        }
        //开始一起走  (pL  != pS ) {一人一步走}
        while (pL != pS) {
            pL = pL.next;
            pS = pS.next;
        }
//        if (pL == null) {
//            return null;
//        }
//        return pL;
        return pL;
    }
10. 给定一个链表,判断链表中是否有环。 
 

判断链表是否有环可以使用快慢指针法,快的每次走两步,慢的每次走一步。若快的和慢的相遇则有环。

 //判断链表是否有环
    public boolean hasCycle() {
        ListNode fast = this.head;
        ListNode slow = this.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;
    }

 //创造一个环
    public void creteLoop() {
        ListNode cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = this.head.next;
    }

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

当slow=fast时,将slow指向头节点。slow和fast同时往后走,当slow和fast相等时的节点就是链表开始入环的第一个节点。

 public ListNode detectCycle() {
        ListNode fast = this.head;
        ListNode slow = this.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 null;
        }
        slow = this.head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

证明:画图

12、给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深拷贝。 

思路分析:
①老新进行进行交替链接
②修改random
③将老新节点打开

 public Node copyRandomList(Node head) {
        if(head == null) {
            return null;
        }
        Node cur = head;
        //1、老新进行进行交替链接
        while(cur != null) {
            Node node = new Node(cur.val, cur.next, null);
            Node tmp = cur.next;
            cur.next = node;
            cur = tmp;
        }
        //2、修改random
        cur = head;
        while(cur != null) {
            if(cur.random != null) {
                cur.next.random = cur.random.next;
                cur = cur.next.next;
            } else {
                cur = cur.next.next;
            }
        }
        //3、将老新节点打开
        cur = head;
        Node newHead = cur.next;
        while(cur.next != null) {
            Node tmp = cur.next;
            cur.next = tmp.next;
            cur = tmp;
        }
        return newHead;
    }

最近做了挺多单链表的题,感觉虽然有的题目思路并不是特别难,但是一不小心就会造成空指针异常,因此做单链表的题要考虑全面,包括单链表的头、尾以及单链表是否为空,next是否为空。。。。

发布了51 篇原创文章 · 获赞 14 · 访问量 2321

猜你喜欢

转载自blog.csdn.net/qq_41185460/article/details/102996216