【數據結構】----順序表與鏈表的應用

!!!排列組合問題 古典概型現在面試的主趨勢
單向鏈表中最重要的是節點,真正有用的值是value,next是維持結構用的

  • 1 第一個節點有特殊性,1)沒有前驅2)代表整個鏈表
  • 2 插入刪除節點時需要前驅節點,除非是第一個節點
  • 3 凡是解引用的地方,需要考慮引用是否爲null
  • 4 方便斷開方便接上
public class ListNode {
    /**
     * @Description: 刪除鏈表中的指定元素{1.考慮指定元素包含了head的情況}
     * 1.
     * 定義新鏈表:Node head表示原鏈表的head;Node result 結果鏈表;
     * Node last 記錄最後一個節點,可以提升尾插的性能
     * 一次遍歷鏈表中的每一個節點,如果節點的值不是要刪除的值,把節點尾插到新鏈表中
     * 2.遍歷鏈表,若果是指定指就刪除
     */
    public ListNode next;
    public int val;

    public ListNode (int val) {
        this.val= val;
    }

   public ListNode removeElements(ListNode head, int val) {
        ListNode result=null;
        ListNode cur=head;
        ListNode last=null;
        while(cur!=null){
            if(cur.val==val){
                cur=cur.next;
            }else{
             
                 ListNode next=cur.next;
                   cur.next=null;
               if(result==null){
                //將節點尾插進入新鏈表
                cur.next=result;
                result=cur;
                last=result;
               }else{
                   last.next=cur;
                   last=cur;
               }
                
                 cur=next;
            }
        }
        return result;
    }

    /**
     * @Description: 反轉單鏈表:定義一個新鏈表,取源鏈表中的每一個節點,頭插到新鏈表的節點中
     * @Param:
     * @return:
     */
    public LinkedNode reversList(LinkedNode head) {
        //定義結果鏈表
        LinkedNode result = null;
        LinkedNode cur = head;
        //進行頭插操作
        //  1-2-3-4
        //1-null;2-1-null;
        while (cur != null) {
            LinkedNode next = cur.next;
            //前插操作:
            // 1.將當前取到的節點作爲結果鏈表的頭元素,頭插進去(node.next=head;this.head=node;)
            // 2.當前元素就成爲了結果鏈表
            cur.next = result;
            result = cur;
            //使循環繼續,利用cur取下一個節點
            cur = next;

        }
        return result;
    }

    /**
     * @Description: 反轉鏈表2:利用三引用反轉
     * @Param: LinkedNode pre=null;LinkedNode cur=head;LinkedNode next=cur.next;
     * @return:
     */
    public LinkedNode reverseList2(LinkedNode head) {

        if (head == null) {
            return null;
        }
        LinkedNode pre = null;
        LinkedNode cur = head;
        //1-2-3-4-2-3
        // p c n
        //3-2-1-null
        while (cur != null) {
            LinkedNode next = cur.next;
            //逆置的過程
            cur.next = pre;
            pre = cur;
            //循環向後走的條件
            cur = next;


        }
        return pre;
    }


    /**
     * @Description: 合併兩個有序鏈表:定義一個新鏈表:分別遍歷兩個鏈表,選擇一個值較小的尾插進新鏈表
     * @Param: LinkedNode next=cur.next; LinkedNode result=null;LinkedNode last=null;
     * @return:
     */
    public LinkedNode mergeTwoList(LinkedNode l1, LinkedNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        LinkedNode cur1 = l1;
        LinkedNode cur2 = l2;
        LinkedNode last = null;
        LinkedNode result = null;

        while (cur1 != null && cur2 != null) {
            if (cur1.value >= cur2.value) {
                LinkedNode next = cur2.next;
                cur2.next = null;
                //取值小的(cur2)尾插操作
                if (result == null) {
                    result = cur2;
                } else {
                    last.next = cur2;
                }
                last = result;
                cur2 = next;
            } else {
                LinkedNode next = cur1.next;
                cur1.next = null;
                if (result == null) {
                    result = cur1;
                } else {
                    last.next = cur1;
                }
                last = result;
                cur1 = next;
            }
        }
        return result;
    }

    /**
     * @Description: 以給定x爲基準將鏈表分割爲兩部分,
     * 所有小於x的節點排在大於或=x的節點前,節點的排列順序不改變
     * <p>
     * 解決思路:定義兩個鏈表:遍歷原鏈表,小於x的節點就放在小的新鏈表中,
     * 大於=x的就放在大的鏈表中,再把兩個鏈表鏈接即可
     */

    public LinkedNode partation(LinkedNode pHead, int x) {
        LinkedNode small = null;
        LinkedNode smallLast = null;
        LinkedNode big = null;
        LinkedNode bigLast = null;
        LinkedNode cur = pHead;
        while (cur != null) {
            LinkedNode next = cur.next;
            if (cur.value < x) {
                //保證尾插的最後一個節點的next爲空
                cur.next = null;
                //如果小鏈表沒有頭節點,當前就是小鏈表的頭節點
                if (small == null) {
                    small = cur;
                } else {
                    //否則就就最後一個節點的下一個,即尾插
                    smallLast.next = cur;
                }
                smallLast = cur;
                cur = next;

            } else {
                cur.next = null;
                if (big == null) {
                    big = cur;
                } else {
                    bigLast.next = cur;
                }
                bigLast = cur;
                cur = next;
            }
        }
        //將大鏈表尾插到小鏈表中{ 1.small爲null ||  2.big爲null}
        LinkedNode result = null;
        if (small == null) {
            return big;
        } else {
            smallLast.next = big;
            return small;
        }
    }


    /**
     * @Description: 給定一個帶有節點head的鏈表,返回鏈表的之間節點,如果有兩個節點,取第二個節點
     * 解決:雙指針遍歷:一個指針跑兩步,另一個指針只跑一步,
     * 因此快的指針走到null節點(末尾)了,慢的指針正好在鏈表之間
     */
    public LinkedNode middleNode(LinkedNode head) {
        LinkedNode fast = head;
        LinkedNode slow = head;
        while (fast != null) {
            fast = fast.next;
            if (fast == null) {
                break;
            }
            slow = slow.next;
            //fast走了兩步,slow只走了一步,
            // 並且保證若是偶數個節點,慢指針值向的是中間的後一個節點
            fast = fast.next;
        }
        return slow;
    }


    /**
     * @Description: 在非空單鏈表中找指定倒數第K個節點
     * 解決:定義一前以後兩個節點,先讓前一個節點先走K步,然後一起前進,
     * 當前面的節點走到null,則就是倒數第K個節點
     * 考慮特殊情況{鏈表正好有m個節點,1.指定k大於m: 2.k等於m}
     * 1-2-3-4-5
     * @return:
     */

    public LinkedNode findKthNode(LinkedNode head, int k) {
        LinkedNode front = head;
        LinkedNode back = head;
        int i = 0;
        for (i = 0; i < k; i++) {
            front = front.next;
        }
        //如果鏈表有5個節點,要找倒數第六個
        if (front == null && i < k) {
            return null;
            //如果鏈表有5個節點,要找倒數第五個
        } else if (front == null) {
            return head;
        }

        while (front != null) {
            front = front.next;
            back = back.next;
        }
        return back;
    }

    /**
     * @Description: 對於一個鏈表,設計一個時間複雜度爲O(N),額外空間複雜度爲O(1)的算法,
     * 判斷其是否爲迴文結構 (ABCCBA)
     * 解決:遍歷鏈表,得到鏈表長度,然後找到中間節點,將中間節點以後的鏈表逆置,
     * 再與新鏈表逐個比較,一旦有不同的節點就說明不是迴文
     */

    public boolean chkPalindrome(LinkedNode A) {
        LinkedNode middle = A;
        int len = getLenght(A);
        for (int i = 0; i < len / 2; i++) {
            //得到之間節點
            middle = middle.next;
        }
        middle = reversList(middle);
        while (A != null && middle != null) {
            if (A.value != middle.value) {
                return false;
            }
            A = A.next;
            middle = middle.next;
        }

        return true;
    }

    private int getLenght(LinkedNode a) {
        int len = 0;
        while (a != null) {
            len++;
            a = a.next;
        }
        return len;
    }

    /**
     * @Description: 在一個排序鏈表中,存在重複的節點,刪除鏈表中重複的節點,
     * 重複的節點不保留,返回鏈表頭指針
     * 解決:兩個指針一前一後,若不相等則一起向前走,
     * 若相等,則前面的節點繼續向前走,走到不相等或爲空爲止,將第二個節點尾插到第一個節點
     * 還需引入一個prev=null,申請一個假節點dummy,作爲處理後的鏈表的頭節點
     * @return: dummy.next
     */

    public LinkedNode deleteDuplication(LinkedNode head) {
        //p1,p2是進行比較的兩個節點
        LinkedNode p1 = head;
        LinkedNode p2 = head.next;
        //假節點用來消除第一個節點沒有前驅的特殊性(如果第一個節點開始就重複便無法處理)
        LinkedNode dummy = new LinkedNode(0);
        dummy.next = head;
        //pre永遠是p1的前一個,用來刪除節點
        LinkedNode pre = dummy;
        //1-2-3-4-4-4-5-6
        //如果值不相等則往前走

        if (head == null) {
            return null;
        }
        while (p2 != null) {
            //因爲有序,p1和p2不相等,和p2的next更不會相等,因此比較可以向前繼續
            if (p1.value != p2.value) {
                pre = pre.next;
                p1 = p1.next;
                p2 = p2.next;
            } else {
                //若果相等則p2向前走,走到不相等爲止
                while (p2.value == p1.value) {
                    p2 = p2.next;
                }
                //將p2指向的節點尾插到pre上
                pre.next = p2;

                p1 = p2;
                p2 = p2.next;

            }
        }
        return dummy.next;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章