資料結構-資料與連結串列
阿新 • • 發佈:2020-07-29
陣列特點:
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);
}
}