国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用

這篇具有很好參考價值的文章主要介紹了數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1. ArrayList存在的問題

  1. ArrayList底層使用連續(xù)的空間,任意位置插入或刪除元素時,需要將該位置后序元素整體往前或者往后搬移,故時間復(fù)雜度為O(N)
  2. 增容需要申請新空間,拷貝數(shù)據(jù),釋放舊空間。會有不小的消耗。
  3. 增容一般是呈2倍的增長,勢必會有一定的空間浪費。例如當前容量為100,滿了以后增容到200,我們再繼續(xù)插入了5個數(shù)據(jù),后面沒有數(shù)據(jù)插入了,那么就浪費了95個數(shù)據(jù)空間。

由于其底層是一段連續(xù)空間,當在ArrayList任意位置插入或者刪除元素時,就需要將后序元素整體往前或者往后搬移,時間復(fù)雜度為O(n),效率比較低,因此ArrayList不適合做任意位置插入和刪除比較多的場景。因此:java集合中又引入了LinkedList,即鏈表結(jié)構(gòu)。

2. 鏈表定義

2.1 鏈表的概念及結(jié)構(gòu)

鏈表是一種常見的數(shù)據(jù)結(jié)構(gòu),它由一系列節(jié)點組成,每個節(jié)點包含兩部分:數(shù)據(jù)元素 (value) 和指向下一個節(jié)點的指針 ( next 域 )。通過這些節(jié)點的連接,可以形成一個鏈式結(jié)構(gòu)。

【單個節(jié)點】:
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
節(jié)點(Node):鏈表的基本單元,包含數(shù)據(jù)域和next指針域。數(shù)據(jù)域可以是任意類型的數(shù)據(jù),指針指向下一個節(jié)點。每個節(jié)點都是一個對象。

【節(jié)點組成的鏈式結(jié)構(gòu)】:
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

結(jié)論: 鏈表是一種物理存儲結(jié)構(gòu)上非連續(xù)存儲結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的引用鏈接次序?qū)崿F(xiàn)的 。

2.2 鏈表的組合類型

1、單向或者雙向

根據(jù)節(jié)點的指針數(shù)量,鏈表可以分為單向鏈表和雙向鏈表。
單向鏈表每個節(jié)點只有一個指針,指向下一個節(jié)點;
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

雙向鏈表每個節(jié)點有兩個指針,分別指向前一個節(jié)點和后一個節(jié)點。
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

2、帶頭或者不帶頭

帶頭節(jié)點的鏈表在第一個節(jié)點之前有一個額外的頭節(jié)點,用于標識鏈表的起始位置。(head的value是無意義的,如果想從最開頭插入數(shù)據(jù)時,head是不可變的,從head后面插入)
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

不帶頭節(jié)點的鏈表則直接以第一個節(jié)點作為鏈表的起始位置。(head是第一個節(jié)點,有value的,如果想從最開頭插入數(shù)據(jù)時,head是可變的,變成新插入的節(jié)點)
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

3、是否循環(huán):

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
循環(huán)鏈表是在鏈表的尾部節(jié)點和頭部節(jié)點之間形成一個循環(huán)連接,使得鏈表的最后一個節(jié)點指向頭部節(jié)點。
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
結(jié)合上述結(jié)構(gòu)可以組合出8中鏈表種類:
單向、帶頭節(jié)點、非循環(huán)鏈表
單向、帶頭節(jié)點、循環(huán)鏈表

單向、不帶頭節(jié)點、非循環(huán)鏈表(重點)
單向、不帶頭節(jié)點、循環(huán)鏈表

雙向、不帶頭節(jié)點、非循環(huán)鏈表(重點)
雙向、不帶頭節(jié)點、循環(huán)鏈表

雙向、帶頭節(jié)點、非循環(huán)鏈表
雙向、帶頭節(jié)點、循環(huán)鏈表

3. 鏈表的實現(xiàn)

3.1 單向、不帶頭、非循環(huán)鏈表的實現(xiàn)

  1. 節(jié)點的定義
	//靜態(tài)內(nèi)部類定義節(jié)點
    public static class ListNode {
        public int data;//數(shù)據(jù)域
        public ListNode next;//指向下一個節(jié)點

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

    public ListNode head; // 表示當前鏈表的頭節(jié)點 方便找到鏈表的第一個元素

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
2. 創(chuàng)建鏈表(窮舉法)

public void createLinkedList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);

        //把鏈表的節(jié)點連起來
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        //使用head節(jié)點來記錄鏈表的入口
        this.head = node1;
    }

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
使用debug,發(fā)現(xiàn)所有節(jié)點鏈接起來了
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
之后,可以使用head節(jié)點,獲取所有的節(jié)點

  1. 打印鏈表

(1) 怎么從一個節(jié)點走向下一個節(jié)點
移動head,走向下一個節(jié)點,使用head = head.next;就可以實現(xiàn)
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
(2)怎么判斷所有節(jié)點都遍歷完
當head指向null時,則所有節(jié)點遍歷完畢
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

 public void display() {
        ListNode cur = this.head;//防止其他方法無法使用head,無法找到鏈表的第一個元素,使用臨時變量cur
        while (cur != null) {//當cur指向null時,則所有節(jié)點遍歷完,循環(huán)結(jié)束
            System.out.print(cur.value + " ");
            cur = cur.next;//不斷移動cur指向下一個節(jié)點
        }
        System.out.println();
    }
  1. 頭插法添加數(shù)據(jù)

頭插法插入數(shù)據(jù)分為三步:
(1)實例化一個節(jié)點
(2)改變插入結(jié)點的next指向原來鏈表的第一個節(jié)點
(3)改變head指向新節(jié)點
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

@Override
    public void addFirst(int data) {

        ListNode listNode = new ListNode(data);
        if (this.head == null) {
            this.head = listNode;
        } else {
            listNode.next = this.head;
            this.head = listNode;
        }
    }
  1. 尾插法添加數(shù)據(jù)

尾插法添加數(shù)據(jù)分為二步:
(1)實例化一個節(jié)點
(2)找到最后一個節(jié)點
(3)將鏈表的原來的最后一個節(jié)點指向新的節(jié)點
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

public void addLast(int data) {
        ListNode listNode = new ListNode(data);
        if (this.head == null) {
            this.head = listNode;
        } else {
            ListNode cur = this.head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = listNode;
        }
    }

【小總結(jié)】:

  • 如果想讓cur指向最后一個節(jié)點的位置,使用cur.next == null
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
  • 如果想把整個鏈表遍歷完成,那么就是cur == null
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

5.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標

在鏈表隨意位置插入數(shù)據(jù)分為四步:
(1)找到該節(jié)點要插入位置的前一個節(jié)點cur(前一個節(jié)點的原因,可以由前一個節(jié)點得到后一個節(jié)點,無法無法從后一個節(jié)點得到前一個結(jié)點),讓cur走index-1步。
(2)實例化一個節(jié)點
(3)讓新插入的節(jié)點的next指向要插入位置的節(jié)點,listNode.next = cur.next;
(4)讓要插入位置的前一個節(jié)點cur的next指向新節(jié)點,cur.next = listNode;
注意:(3)和(4)的順序要正確,再插入一個節(jié)點時,一定要先綁定后面這個節(jié)點
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

 public void addIndex(int index, int data) {
        if(index<0||index>size()){//要先判斷index的合法性
            throw new  IndexOutOfBounds("鏈表下標越界");//自定義異常鏈表下標越界
        }
        if(index == 0){//在第一個位置插入,使用頭插法的方法
            addFirst(data);
        }else if(index == size()){//在最后一個位置插入,使用尾插法的方法
            addLast(data);
        }else {//在中間位置插入
            ListNode listNode = new ListNode(data);//要插入的節(jié)點
            ListNode cur = searchPrev(index);//要插入位置的前一個節(jié)點
            listNode.next = cur.next;
            cur.next = listNode;
        }
    }

    private ListNode searchPrev(int index){
        ListNode cur = this.head;
        int count = 0;
        while (count != index -1){
            cur = cur.next;
            count++;
        }
        return cur;
    }

  1. 刪除第一次出現(xiàn)關(guān)鍵字為key的節(jié)點
    刪除鏈表中的節(jié)點分為三步:
    (1)找到要刪除節(jié)點的前一個節(jié)點cur
    (2) 標記要刪除的要素(也可以不要這一步)ListNode del = cur.next;
    (3)讓cur的next指向要刪除的節(jié)點的下一個節(jié)點(要刪除的節(jié)點沒有被引用,JVM會自動回收)
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
    在第一步中找到要刪除節(jié)點的前一個節(jié)點cur,需要找到cur.next.value 和 key的值相等的節(jié)點,如果是引用類型時,現(xiàn)需要使用equals()方法,而不是==
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
    還有一種情況,當要刪除的數(shù)據(jù)是第一個節(jié)點時,只需要讓head指向第二個節(jié)點即可做到刪除第一個節(jié)點
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
 	public void remove(int key) {
 	//當要刪除的數(shù)據(jù)是第一個節(jié)點
        if (this.head.value == key) {
            this.head = this.head.next;
        } else {
        //找到前驅(qū)
            ListNode cur = getPrev(key);
            //判斷前驅(qū)是否存在
            if (cur == null) {
                System.out.println("要刪除的數(shù)字不存在");
            } else {
            //刪除節(jié)點
                ListNode del = cur.next;
                cur.next = del.next;
            }
        }
    }

    private ListNode getPrev(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.next.value == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }
  1. 刪除所有值為key的節(jié)點

刪除所有值為key的節(jié)點分為三步:
(1)遍歷鏈表,找到要刪除節(jié)點cur
(2) 讓cur的上一個節(jié)點指向cur的下一個節(jié)點,刪除cur
(3)繼續(xù)遍歷鏈表,找到所有與key相等的節(jié)點,重復(fù)(1)(2)步驟,直至遍歷完鏈表

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

還有要考慮一種情況,當要刪除的數(shù)據(jù)是第一個節(jié)點時,只需要讓head指向第二個節(jié)點即可做到刪除第一個節(jié)點
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

public void removeAllKey(int key) {
        if(this.head == null){
            return;
        }
        ListNode prev = this.head;
        ListNode cur = this.head.next;
        while (cur != null) {
            if (cur.value == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        if (this.head.value == key) {
            this.head = this.head.next;
            return;
        }
    }
  1. 清空鏈表

清空鏈表分為兩種方式:
方式一:直接將head置為null(不推薦)
方式二:遍歷節(jié)點,將每個節(jié)點的數(shù)值域和next域置為空
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

public void clear() {
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
//            cur.value = null;//  引用類型value要置為null
            cur.next = null;
            cur = curNext;
        }
        this.head = null;
    }

完整代碼:
ILst接口:

public interface IList {
    //頭插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
    public void addIndex(int index, int data);
    //查找是否包含關(guān)鍵字key是否在單鏈表當中
    public boolean contains(int key);
    //刪除第一次出現(xiàn)關(guān)鍵字為key的節(jié)點
    public void remove(int key);
    //刪除所有值為key的節(jié)點
    public void removeAllKey(int key);
    //得到單鏈表的長度
    public int size();
    public void clear();
    public void display();
}

自定義異常IndexOutOfBounds類

public class IndexOutOfBounds extends RuntimeException{
    public IndexOutOfBounds(String message) {
        super(message);
    }
}

MyLinkedList類:

public class MyLinkedList implements IList {
    //靜態(tài)內(nèi)部類
    public static class ListNode {
        public int value;
        public ListNode next;

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

    public ListNode head; //方便找到鏈表的第一個元素

    public void createLinkedList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);

        //把表節(jié)點連起來
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        //使用head節(jié)點來記錄鏈表的入口
        this.head = node1;

    }

    //頭插法
    @Override
    public void addFirst(int data) {

        ListNode listNode = new ListNode(data);
        if (this.head == null) {
            this.head = listNode;
        } else {
            listNode.next = this.head;
            this.head = listNode;
        }
    }

    //尾插法
    @Override
    public void addLast(int data) {
        ListNode listNode = new ListNode(data);
        if (this.head == null) {
            this.head = listNode;
        } else {
            ListNode cur = this.head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = listNode;
        }
    }

    @Override
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {//要先判斷index的合法性
            throw new IndexOutOfBounds("鏈表下標越界");//自定義異常鏈表下標越界
        }
        if (index == 0) {//在第一個位置插入,使用頭插法的方法
            addFirst(data);
        } else if (index == size()) {//在最后一個位置插入,使用尾插法的方法
            addLast(data);
        } else {//在中間位置插入
            ListNode listNode = new ListNode(data);//要插入的節(jié)點
            ListNode cur = searchPrev(index);//要插入位置的前一個節(jié)點
            listNode.next = cur.next;
            cur.next = listNode;
        }

    }

    private ListNode searchPrev(int index) {
        ListNode cur = this.head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

    @Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.value == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    @Override
    public void remove(int key) {
        if (this.head.value == key) {
            this.head = this.head.next;
        } else {
            ListNode cur = getPrev(key);
            if (cur == null) {
                System.out.println("要刪除的數(shù)字不存在");
            } else {
                ListNode del = cur.next;
                cur.next = del.next;
            }
        }
    }

    private ListNode getPrev(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.next.value == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    @Override
    public void removeAllKey(int key) {
        if (this.head == null) {
            return;
        }
        ListNode prev = this.head;
        ListNode cur = this.head.next;
        while (cur != null) {
            if (cur.value == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        if (this.head.value == key) {
            this.head = this.head.next;
            return;
        }
    }

    @Override
    public int size() {
        int count = 0;
        ListNode cur = this.head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    @Override
    public void clear() {
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
//            cur.value = null;
            cur.next = null;
            cur = curNext;
        }
        this.head = null;
    }

    @Override
    public void display() {
        ListNode cur = this.head;
        while (cur != null) {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

3.2 雙向、不帶頭節(jié)點、非循環(huán)鏈表的實現(xiàn)

  1. 結(jié)點的定義
    static class ListNode {
        public int val;//數(shù)值域
        public ListNode next;//指針域,指向下一個節(jié)點
        public ListNode prev;//指針域,指向前一個節(jié)點

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

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
2. 頭插法
第一步:讓新插入的next 指向head節(jié)點
第二步:讓head節(jié)點的prev指向新插入的節(jié)點
第三步:讓head指向新插入的節(jié)點
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

 public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if(head == null){
            head = node;
            last = node;
        }else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }
  1. 尾插法
    第一步:讓last節(jié)點的next指向新插入的節(jié)點
    第二步:讓新插入的prev指向last節(jié)點
    第三步:讓last指向新插入的節(jié)點
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
 public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        } else {
            last.next = node;
            node.prev = last;
            last = node;
        }
    }
  1. 在指定位置插入元素
    第一步:判斷要插入的位置是否合法(0<=index<=鏈表的長度)
    第二步:判斷要插入的節(jié)點是否在0位置,如果在0位置,那么就使用頭插法
    第三步:判斷要插入的節(jié)點是否在最后一個位置,如果在最后一個位置,那么就使用尾插法
    第四步:如果要在中間插入,需要插入位置前一個節(jié)點的next域指向新節(jié)點,新節(jié)點的next域指向插入位置的原來的節(jié)點,新節(jié)點的prev域指向需要插入位置前一個節(jié)點,需要插入位置的原來的節(jié)點的prev域指向新節(jié)點,改變四個指針域的指向便可以將新節(jié)點插入需要插入的位置。
    數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
	public void addIndex(int index, int data) {
        int len = size();
        //index小于0,或者大于鏈表的長度,插入位置不合法
        if (index < 0 || index > len) {
            throw new IndexOutOfBounds("要插入的位置小于0或者超過鏈表的長度,該位置不合法");
        }
        //如果在第一個節(jié)點插入,使用頭插法
        if (index == 0) {
            addFirst(data);
            return;
        }
        //如果在最后一個節(jié)點插入,使用尾插法
        if (index == len) {
            addLast(data);
            return;
        }
        //中間位置
        //獲取要插入的位置的節(jié)點
        ListNode cur = getNode(index);
        //新插入的節(jié)點
        ListNode node = new ListNode(data);
        //在中間位置插入節(jié)點元素
        ListNode prev = cur.prev;
        prev.next = node;
        node.next = cur;
        node.prev = prev;
        cur.prev = node;
    }
    public ListNode getNode(int index) {
        ListNode cur = this.head;
        while (index != 0) {
            index--;
            cur = cur.next;
        }
        return cur;
    }

5.刪除指定節(jié)點
總共分為三種情況,分別如下:
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
特殊情況:還需要考慮一種特殊情況,當量表中只存在一個節(jié)點,并且該結(jié)點時需要刪除的節(jié)點的情況
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

    public void  remove(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            //判斷是否找到目標節(jié)點
            if(cur.val == key) {
                //要刪除的節(jié)點是頭節(jié)點的情況
                if(cur == head) {
                    head = head.next;
                    //鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
                    if(head == null) {
                        last = null;
                    }else {
                        //鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
                        head.prev = null;
                    }
                    return;
                }
                //要刪除的節(jié)點是尾節(jié)點的情況
                if(cur == last) {
                    cur.prev.next = cur.next;
                    last = last.prev;
                    return;
                }
                //要刪除的節(jié)點是中間的節(jié)點的情況
                cur.prev.next = cur.next;
                cur.next.prev = cur.prev;
                return;
            }else {
                cur = cur.next;
            }
        }
        System.out.println("該節(jié)點不存在");
    }
  1. 鏈表置空
    方式一:
		head=null; 
		last=null;

方式二:
1.使用cur局部變量,遍歷鏈表,將每一個節(jié)點都置為null
2.將頭節(jié)點和尾節(jié)點置為空

數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

public void clear() {
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
            //如果是引用類型,還需要將數(shù)值域置為空
//            cur.val = null;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        head = null;
        last = null;
    }

【完整代碼】:
雙向、不帶頭節(jié)點、非循環(huán)鏈表的實現(xiàn):LinkedList .class

public class LinkedList2 implements IList {
    static class ListNode {
        public int val;//數(shù)值域
        public ListNode next;//指針域,指向下一個節(jié)點
        public ListNode prev;//指針域,指向前一個節(jié)點

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

    public ListNode head;
    public ListNode last;

    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        } else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            last = node;
        } else {
            last.next = node;
            node.prev = last;
            last = node;
        }
    }

    @Override
    public void addIndex(int index, int data) {
        int len = size();
        //index小于0,或者大于鏈表的長度,插入位置不合法
        if (index < 0 || index > len) {
            throw new IndexOutOfBounds("要插入的位置小于0或者超過鏈表的長度,該位置不合法");
        }
        //如果在第一個節(jié)點插入,使用頭插法
        if (index == 0) {
            addFirst(data);
            return;
        }
        //如果在最后一個節(jié)點插入,使用尾插法
        if (index == len) {
            addLast(data);
            return;
        }
        //中間位置
        //獲取要插入的位置的節(jié)點
        ListNode cur = getNode(index);
        //新插入的節(jié)點
        ListNode node = new ListNode(data);
        //在中間位置插入節(jié)點元素
        ListNode curPrev = cur.prev;
        curPrev.next = node;
        node.next = cur;
        node.prev = curPrev;
        cur.prev = node;
    }

    public ListNode getNode(int index) {
        ListNode cur = this.head;
        while (index != 0) {
            index--;
            cur = cur.next;
        }
        return cur;
    }

    @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    @Override
    public void remove(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            //判斷是否找到目標節(jié)點
            if (cur.val == key) {
                //要刪除的節(jié)點是頭節(jié)點的情況
                if (cur == head) {
                    head = head.next;
                    //鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
                    if (head == null) {
                        last = null;
                    } else {
                        //鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
                        head.prev = null;
                    }
                } else if (cur == last) {
                    //要刪除的節(jié)點是尾節(jié)點的情況
                    cur.prev.next = cur.next;
                    last = last.prev;
                } else {
                    //要刪除的節(jié)點是中間的節(jié)點的情況
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
                return;
            } else {
                cur = cur.next;
            }
        }
        System.out.println("該節(jié)點不存在");
    }

    @Override
    public void removeAllKey(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            //判斷是否找到目標節(jié)點
            if (cur.val == key) {
                //要刪除的節(jié)點是頭節(jié)點的情況
                if (cur == head) {
                    head = head.next;
                    //鏈表中只有頭節(jié)點一個節(jié)點,刪除之后鏈表為空鏈表,last需要置為null
                    if (head == null) {
                        last = null;
                    } else {
                        //鏈表中不止一個節(jié)點,需要將頭節(jié)點的prev置為null
                        head.prev = null;
                    }
                } else if (cur == last) {
                    //要刪除的節(jié)點是尾節(jié)點的情況
                    cur.prev.next = cur.next;
                    last = last.prev;
                } else {
                    //要刪除的節(jié)點是中間的節(jié)點的情況
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
                cur = cur.next;

            }
            System.out.println("該節(jié)點不存在");
        }
    }

    @Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    @Override
    public void clear() {
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
//            cur.val = null;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        head = null;
        last = null;
    }

    @Override
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

4.LinkedList的使用

4.1 什么是LinkedList

LinkedList 的官方文檔
LinkedList的底層是雙向鏈表結(jié)構(gòu)(鏈表后面介紹),由于鏈表沒有將元素存儲在連續(xù)的空間中,元素存儲在單獨的節(jié)點中,然后通過引用將節(jié)點連接起來了,因此在在任意位置插入或者刪除元素時,不需要搬移元素,效率比較高。
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java
在集合框架中,LinkedList也實現(xiàn)了List接口,具體如下:
數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),鏈表,java

【說明】

1. LinkedList實現(xiàn)了List接口
2. LinkedList的底層使用了雙向鏈表
3. LinkedList沒有實現(xiàn)RandomAccess接口,因此LinkedList不支持隨機訪問
4. LinkedList的任意位置插入和刪除元素時效率比較高,時間復(fù)雜度為O(1)
5. LinkedList比較適合任意位置插入的場景
文章來源地址http://www.zghlxwxcb.cn/news/detail-832182.html

4.2 LinkedList的使用

4.2.1. LinkedList的構(gòu)造

方法 解釋
LinkedList() 無參構(gòu)造
public LinkedList(Collection<? extends E> c) 使用其他集合容器中元素構(gòu)造List
public static void main(String[] args) {
    // 構(gòu)造一個空的LinkedList
    List<Integer> list1 = new LinkedList<>();
    
    List<String> list2 = new java.util.ArrayList<>();
    list2.add("JavaSE");
    list2.add("JavaWeb");
    list2.add("JavaEE");
    // 使用ArrayList構(gòu)造LinkedList
    List<String> list3 = new LinkedList<>(list2);
}

4.2.2. LinkedList的其他常用方法介紹

方法 解釋
boolean add(E e) 尾插 e
void add(int index, E element) 將 e 插入到 index 位置
boolean addAll(Collection<? extends E> c) 尾插 c 中的元素
E remove(int index) 刪除 index 位置元素
boolean remove(Object o) 刪除遇到的第一個 o
E get(int index) 獲取下標 index 位置元素
E set(int index, E element) 將下標 index 位置元素設(shè)置為 element
void clear() 清空
boolean contains(Object o) 判斷 o 是否在線性表中
int indexOf(Object o) 返回第一個 o 所在下標
int lastIndexOf(Object o) 返回最后一個 o 的下標
List subList(int fromIndex, int toIndex) 截取部分 list
public static void main(String[] args) {
    LinkedList<Integer> list = new LinkedList<>();
    list.add(1);   // add(elem): 表示尾插
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    System.out.println(list.size());
    System.out.println(list);
    
    // 在起始位置插入0
    list.add(0, 0);  // add(index, elem): 在index位置插入元素elem
    System.out.println(list);
    
    list.remove();         // remove(): 刪除第一個元素,內(nèi)部調(diào)用的是removeFirst()
    list.removeFirst();    // removeFirst(): 刪除第一個元素
    list.removeLast();    // removeLast(): 刪除最后元素
    list.remove(1);  // remove(index): 刪除index位置的元素
    System.out.println(list);
    
    // contains(elem): 檢測elem元素是否存在,如果存在返回true,否則返回false
    if(!list.contains(1)){
        list.add(0, 1);
   }
   list.add(1);
    System.out.println(list);
    System.out.println(list.indexOf(1));   // indexOf(elem): 從前往后找到第一個elem的位置
    System.out.println(list.lastIndexOf(1));  // lastIndexOf(elem): 從后往前找第一個1的位置
    int elem = list.get(0);    // get(index): 獲取指定位置元素
    list.set(0, 100);          // set(index, elem): 將index位置的元素設(shè)置為elem
    System.out.println(list);
    
    // subList(from, to): 用list中[from, to)之間的元素構(gòu)造一個新的LinkedList返回
    List<Integer> copy = list.subList(0, 3);   
    System.out.println(list);
    System.out.println(copy);
    list.clear();              // 將list中元素清空
    System.out.println(list.size());
}

4.2.3. LinkedList的遍歷

public static void main(String[] args) {
    LinkedList<Integer> list = new LinkedList<>();
    list.add(1);   // add(elem): 表示尾插
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    System.out.println(list.size());
    
    //方式一: foreach遍歷
    for (int e:list) {
        System.out.print(e + " ");
   }
    System.out.println();
    
    //方式二:for循環(huán)
    for (int i = 0;i < list.size();i++) {
        System.out.print(list.get(i) + " ");
   }
    System.out.println();
    
    //方式三:迭代器
    // 使用迭代器遍歷---正向遍歷
    ListIterator<Integer> it = list.listIterator();
    while(it.hasNext()){
        System.out.print(it.next()+ " ");
   }
    System.out.println();
    // 使用反向迭代器---反向遍歷
    ListIterator<Integer> rit = list.listIterator(list.size());
    while (rit.hasPrevious()){
        System.out.print(rit.previous() +" ");
   }
    System.out.println();
}

5. ArrayList和LinkedList的區(qū)別

不同點 ArrayList LinkedList
存儲空間上 物理上一定連續(xù) 邏輯上連續(xù),但物理上不一定連續(xù)
隨機訪問 支持O(1) 不支持:O(N)
頭插 需要搬移元素,效率低O(N) 只需修改引用的指向,時間復(fù)雜度為O(1)
插入 空間不夠時需要擴容 沒有容量的概念
應(yīng)用場景 元素高效存儲+頻繁訪問 任意位置插入和刪除頻繁
  • 如果是經(jīng)常根據(jù)下標進行查找使用順序表(ArrayList)
  • 如果是經(jīng)常插入和刪除操作的可以使用鏈表(LinkedList)

到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu)----鏈表介紹、模擬實現(xiàn)鏈表、鏈表的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 數(shù)據(jù)結(jié)構(gòu)——鏈表的實現(xiàn)(Java版)

    目錄 一、鏈表 二、代碼實現(xiàn) 1.創(chuàng)建結(jié)點 2.構(gòu)造函數(shù) 3.鏈表的相關(guān)屬性 4.添加虛擬節(jié)點 5.判斷鏈表是否為空 6.添加方法 (1)在頭部添加 (2)在尾部添加 (3)在索引位置添加 (4)對頭插法和尾插法代碼進行簡化(調(diào)用任意位置添加的方法) 7.打印鏈表(遍歷,重寫toString方

    2024年01月23日
    瀏覽(24)
  • 數(shù)據(jù)結(jié)構(gòu):雙向鏈表的實現(xiàn)(C實現(xiàn))

    數(shù)據(jù)結(jié)構(gòu):雙向鏈表的實現(xiàn)(C實現(xiàn))

    個人主頁 : 個人主頁 個人專欄 : 《數(shù)據(jù)結(jié)構(gòu)》 《C語言》 本篇博客,將要實現(xiàn)的是 帶頭雙向循環(huán)鏈表 ,該結(jié)構(gòu)實現(xiàn)尾插,尾刪只需要O(1)的時間復(fù)雜度。 其結(jié)構(gòu)如下所示: 既然要實現(xiàn)的鏈表是雙向循環(huán)的,那么對于指針域,我們就需要 指向節(jié)點上一個的指針 和 指向節(jié)點

    2024年02月14日
    瀏覽(22)
  • 數(shù)據(jù)結(jié)構(gòu)中鏈表的實現(xiàn)以及排序

    本期和大家主要分享的是關(guān)于數(shù)據(jù)結(jié)構(gòu)中雙向鏈表的實現(xiàn)過程,那么話不多說,來具體看看吧! 來分析一下,這里呢定義了一個int的數(shù)據(jù)類型,表明整個鏈表存放的是整形的數(shù)據(jù);其次定義了鏈表節(jié)點的數(shù)據(jù)類型,其中包括了此節(jié)點存放的數(shù)據(jù)以及鏈接向下一個節(jié)點的地址;

    2024年02月02日
    瀏覽(20)
  • 【數(shù)據(jù)結(jié)構(gòu)】 鏈表簡介與單鏈表的實現(xiàn)

    【數(shù)據(jù)結(jié)構(gòu)】 鏈表簡介與單鏈表的實現(xiàn)

    在【數(shù)據(jù)結(jié)構(gòu)】 ArrayList簡介與實戰(zhàn)中我們已經(jīng)熟悉了ArrayList的使用,并且進行了簡單模擬實現(xiàn)。通過源碼知道,ArrayList底層使用數(shù)組來存儲元素 由于其底層是一段連續(xù)空間, 當在ArrayList任意位置插入或者刪除元素時,就需要將后序元素整體往前或者往后搬移,時間復(fù)雜度為

    2024年02月12日
    瀏覽(1202)
  • 【數(shù)據(jù)結(jié)構(gòu)】—帶頭雙向循環(huán)鏈表的實現(xiàn)(完美鏈表)

    【數(shù)據(jù)結(jié)構(gòu)】—帶頭雙向循環(huán)鏈表的實現(xiàn)(完美鏈表)

    鏈表結(jié)構(gòu)一共有八種形式,在前面的文章里已經(jīng)講完了不帶頭單向非循環(huán)鏈表的實現(xiàn),但是我們發(fā)現(xiàn)該鏈表實現(xiàn)尾插與尾刪時比較麻煩,要先從頭節(jié)點進行遍歷,找到尾節(jié)點,時間復(fù)雜度為O(N),而本次所講的帶頭雙向循環(huán)單鏈表,則可以直接找到尾節(jié)點。 雖然該鏈表看起來

    2024年01月25日
    瀏覽(18)
  • c語言數(shù)據(jù)結(jié)構(gòu)——鏈表的實現(xiàn)及其基本操作

    c語言數(shù)據(jù)結(jié)構(gòu)——鏈表的實現(xiàn)及其基本操作

    順序表的問題及思考 問題: 中間/頭部的插入刪除,時間復(fù)雜度為O(N) 增容需要申請新空間,拷貝數(shù)據(jù),釋放舊空間。會有不小的消耗。 增容一般是呈2倍的增長,勢必會有一定的空間浪費。例如當前容量為100,滿了以后增容到 200,我們再繼續(xù)插入了5個數(shù)據(jù),后面沒有數(shù)據(jù)插

    2023年04月09日
    瀏覽(97)
  • [C語言][數(shù)據(jù)結(jié)構(gòu)][鏈表] 單鏈表的從零實現(xiàn)!

    [C語言][數(shù)據(jù)結(jié)構(gòu)][鏈表] 單鏈表的從零實現(xiàn)!

    目錄 零.必備知識 1.一級指針 二級指針 2. 節(jié)點的成員列表 ? ? a.數(shù)據(jù) ? ? b.指向下一個節(jié)點的指針. 3. 動態(tài)內(nèi)存空間的開辟 (malloc-calloc-realloc) 一.單鏈表的實現(xiàn)與銷毀? ? ? ? ? 1.1 節(jié)點的定義 ? ? ? ? 1.2 單鏈表的尾插 ? ? ? ? 1.3 單鏈表的頭插 ? ? ? ? 1.4 單鏈表的尾刪 ?

    2024年04月11日
    瀏覽(94)
  • [C語言][數(shù)據(jù)結(jié)構(gòu)][鏈表] 雙鏈表的從零實現(xiàn)!

    [C語言][數(shù)據(jù)結(jié)構(gòu)][鏈表] 雙鏈表的從零實現(xiàn)!

    目錄 零.必備知識 0.1 一級指針 二級指針 0.2 雙鏈表節(jié)點的成員列表 ? ? ? ? a. 數(shù)據(jù) ? ? ? ? b. 后驅(qū)指針 ? ? ? ? c. 前驅(qū)指針 0.3 動態(tài)內(nèi)存空間的開辟 一. 雙鏈表的實現(xiàn)與銷毀 ? ? ? ? 1.1 節(jié)點的定義 ????????1.2 雙向鏈表的初始化 創(chuàng)建新節(jié)點 ????????1.3 尾插? ? ? ?

    2024年04月17日
    瀏覽(1215)
  • 一起學(xué)數(shù)據(jù)結(jié)構(gòu)(3)——萬字解析:鏈表的概念及單鏈表的實現(xiàn)

    一起學(xué)數(shù)據(jù)結(jié)構(gòu)(3)——萬字解析:鏈表的概念及單鏈表的實現(xiàn)

    上篇文章介紹了數(shù)據(jù)結(jié)構(gòu)的一些基本概念,以及順序表的概念和實現(xiàn),本文來介紹鏈表的概念和單鏈表的實現(xiàn),在此之前,首先來回顧以下順序表的特點: 1. 順序表是一組地址連續(xù)的存儲單元依次存儲的線性表的數(shù)據(jù)結(jié)構(gòu),邏輯上:順序表中相鄰的數(shù)據(jù)元素,其物理次序也是

    2024年02月13日
    瀏覽(92)
  • 鏈表的總體涵蓋以及無哨兵位單鏈表實現(xiàn)——【數(shù)據(jù)結(jié)構(gòu)】

    鏈表的總體涵蓋以及無哨兵位單鏈表實現(xiàn)——【數(shù)據(jù)結(jié)構(gòu)】

    ???W…Y:個人主頁 在學(xué)習(xí)之前看一下美麗的夕陽,也是很不錯的。 如果覺得博主的美景不錯,博客也不錯的話,關(guān)注一下博主吧?? 在上一期中,我們說完了順序表,并且提出順序表中的問題 1. 中間/頭部的插入刪除,時間復(fù)雜度為O(N) 2. 增容需要申請新空間,拷貝數(shù)據(jù),釋

    2024年02月14日
    瀏覽(163)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包