1. 程式人生 > 實用技巧 >資料結構-資料與連結串列

資料結構-資料與連結串列

陣列特點:
        1. 線性表,資料排成像線一樣的資料結構,資料只有前後倆個方向,陣列,連結串列,佇列,棧都是線性表
        2. 連續的記憶體空間,讓它支援隨機訪問,根據下標隨機訪問的時間複雜度為o(1),隨機訪問的公式
        3. 相同型別的資料結構
        4. 隨機訪問公式,a[i]_address = base_address + i * data_type_size,其中data_type_size為資料型別大小,eg:int四位元組
陣列刪除和插入效率低的原因
        1. 插入或者刪除;都會導致後續的資料大規模移動
        2. 如果進行優化   1. 比如刪除的時候,將要刪除的資料和最後一位調換位置,在刪除;
                         2. 或者類似jvm的標記刪除發,先標記,等陣列空間不夠了,在真正刪除,
                         3. 以上都是減少了大規模資料移動問題
陣列越界問題
        1. 並非所有的語言都會對陣列越界進行檢查,比如c,即使越界,但只要偏移計算後的記憶體可訪問,那麼程式就不會報錯,所以病毒就可以利用這點進行非法訪問
陣列與容器
        1. 拿Java來說 int[] 對應的容器是 arraylist,由於 arraylist 無法儲存基礎型別 ,所以需要int轉化成Integer ,這個轉化的就是裝箱和拆箱,有一定的效能消耗
        2. arraylist,可以自動擴容,封裝了很多方法,隱藏了操作細節,使用也更方便
        3. 如果僅僅是業務開發,容器就很好,那點效能消耗相較於系統整體效能無上大雅
        4. 但是如果是底層,就需要用陣列,畢竟這種效能,能扣一點是一點
        5. 另外,如果刷演算法題,建議用陣列,這樣可以更直觀感受陣列的變化,操作的細節。
陣列為什麼下標從0開始
        1. 下標,也可以叫偏移,a[]中,a是首地址,a[0]代表偏移為0個size的地址
連結串列的定義:
        1. 不同於陣列,連結串列需要的可以不是連續的記憶體空間,只要總大小夠即可
        2. 連結串列中每一個節點之間是通過指標連結
        3. 連結串列分為 單向連結串列,雙向連結串列,雙向連結串列
        4. 說一種簡單的連結串列結構,如單向連結串列,
            ```
           -> data,next ->....;
            ```      
        5. 連結串列的插入和刪除只需要更改一個節點
陣列和連結串列的對比
        1. 記憶體大小,連結串列每個節點需要額外的空間儲存指向下一個節點的指標,多以同等資料規模下,陣列記憶體消耗小
        2. 插入和刪除的效能, 陣列 o(n),連結串列o(1)
        3. 隨機訪問, 陣列 o(1),連結串列o(n)
        4. 記憶體的申請上,陣列需要連續的空間,連結串列可以是零散的空間
        5. 記憶體碎片,連結串列頻繁申請釋放記憶體,會導致記憶體碎片,容易頻繁出發gc      
leetcode上陣列經典題目
//給你一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複
//的三元組。 
//
// 注意:答案中不可以包含重複的三元組。 
//
// 
//
// 示例: 
//
// 給定陣列 nums = [-1, 0, 1, 2, -1, -4],
//
//滿足要求的三元組集合為:
//[
//  [-1, 0, 1],
//  [-1, -1, 2]
//]
// 
// Related Topics 陣列 雙指標

package leetcode.editor.cn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

//Java:三數之和
public class P15ThreeSum {
    public static void main(String[] args) {
        Solution solution = new P15ThreeSum().new Solution();
        // TO TEST
        int[] nums = {-1, 0, 1, 2, -1, -4};
        List<List<Integer>> res = solution.threeSum(nums);
        res.forEach(n -> {
            n.forEach(m -> {
                System.out.print(m + " ");
            });
        });
    }

    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            if (nums == null || nums.length == 0) {
                return new ArrayList<>();
            }
            Arrays.sort(nums);
            List<List<Integer>> res = new ArrayList<>();
            for (int i = 0; i < nums.length; i++) {
                if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) {
                    int l = i + 1;
                    int r = nums.length - 1;
                    while (l < r) {
                        int temp = nums[i] + nums[l] + nums[r];
                        if (temp == 0) {
                            res.add(Arrays.asList(nums[i], nums[l], nums[r]));
                            while (l < r && nums[l] == nums[l + 1]) l++;
                            while (l < r && nums[r] == nums[r - 1]) r--;
                            l++;
                            r--;
                        } else if (temp < 0) {
                            while(l<r && nums[l] == nums[l+1]) l++;
                            l++;
                        } else {
                            while(l<r && nums[r] == nums[r-1]) r--;
                            r--;
                        }
                    }
                }
            }
            return res;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}
leetcode連結串列經典題目
//合併 k 個排序連結串列,返回合併後的排序連結串列。請分析和描述演算法的複雜度。 
//
// 示例: 
//
// 輸入:
//[
//  1->4->5,
//  1->3->4,
//  2->6
//]
//輸出: 1->1->2->3->4->4->5->6 
// Related Topics 堆 連結串列 分治演算法

package leetcode.editor.cn;

import com.example.demo.ListNode;

//Java:合併K個排序連結串列
public class P23MergeKSortedLists{
    public static void main(String[] args) {
        Solution solution = new P23MergeKSortedLists().new Solution();
        // TO TEST
    }
    //leetcode submit region begin(Prohibit modification and deletion)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        ListNode res = null;
        for (int i = 0; i < lists.length; i++) {
            res =  merge(res,lists[i]);
        }
        return res;

    }

    //依次合併連結串列
    private ListNode merge(ListNode l1, ListNode l2){
        if(l1==null){
            return l2;
        }

        if(l2==null){
            return l1;
        }

        if(l1.val<l2.val){
           l1.next = merge(l1.next,l2);
           return l1;
        }else{
          l2.next =merge(l1,l2.next);
          return l2;
        }
    }
}
//leetcode submit region end(Prohibit modification and deletion)

}
連結串列還有一個經典的例子 LRU
package com.example.demo;


/**
 * 有序單鏈表實現lru
 * 思路:1.如果此資料之前已經被快取在連結串列中了,我們遍歷得到這個資料對應的結點,並將其從原來的位置刪除,然後再插入到連結串列的頭部。
 * 2.如果此資料沒有在快取連結串列中,又可以分為兩種情況:
 * 如果此時快取未滿,則將此結點直接插入到連結串列的頭部;
 * 如果此時快取已滿,則連結串列尾結點刪除,將新的資料結點插入連結串列的頭部。
 */
public class Lc_LRU {
    public static class LinkList {
        int node;
        LinkList next;

        public LinkList() {
        }

        public LinkList(int node, LinkList next) {
            this.node = node;
            this.next = next;
        }

        public LinkList(int node) {
            this.node = node;
        }

        public int getNode() {
            return node;
        }

        public void setNode(int node) {
            this.node = node;
        }

        public LinkList getNext() {
            return next;
        }

        public void setNext(LinkList next) {
            this.next = next;
        }
    }

    //連結串列最大長度
    int size = 10;

    //遍歷連結串列
    private boolean traversal(LinkList newNode, LinkList linkList) {
        LinkList curr = linkList;
        while (curr != null) {
            if (curr.node != newNode.node) {
                curr = curr.next;
            } else {
                return true;
            }
        }
        return false;
    }

    //遍歷連結串列是否存在新插入的值,存在則刪除
    private LinkList delete(LinkList head, LinkList toRemove) {
       if(head.node == toRemove.node){
           return head.next;
       }
       LinkList pre = head;
       LinkList curr = head;
       while(curr!=null){
           if(curr.node == toRemove.node){
               pre.next = curr.next;
               return pre;
           }else{
               pre = curr;
           }
           curr = curr.next;
       }
       return null;
    }

    //刪除最後一個節點
    private void deleteLastNode(LinkList linkList) {
        LinkList curr = linkList;
        int position = 0;
        while (curr != null && position != 8) {
            position++;
            curr = curr.next;
        }
        curr.next = null;
    }


    //遍歷連結串列長度
    private int count(LinkList linkList) {
        LinkList curr = linkList;
        int count = 0;
        while (curr != null) {
            count++;
            curr = curr.next;
        }
        return count;
    }

    //插入新節點,檢查是否存在該節點,若存在刪除原來位置的節點,插入到頭,如果不存在,直接插入到頭
    private LinkList insert(LinkList newNode, LinkList linkList) {
        if (count(linkList) < 10) {
            //go on
        } else {
            deleteLastNode(linkList);
        }
        linkList = delete(linkList,newNode);
        LinkList head = newNode;
        head.next = linkList;
        return head;
    }

    private void sysoLinkList(LinkList linkList) {
        StringBuffer stringBuffer = new StringBuffer();
        LinkList curr = linkList;
        while (curr != null) {
            stringBuffer.append(curr.node);
            curr = curr.next;
        }
        System.out.println(stringBuffer.toString());
    }

    public static void main(String[] args) {
        LinkList linkList = new LinkList(2);
        LinkList newNode = new LinkList(1);
        linkList.next = newNode;
        LinkList newNode1 = new LinkList(1);
        Lc_LRU lc_lru = new Lc_LRU();
        linkList = lc_lru.insert(newNode1, linkList);
        lc_lru.sysoLinkList(linkList);
    }
}