首頁 > 軟體

Java實現順序表和連結串列結構

2022-02-11 10:03:48

前言:

線性表(linear list)是n個具有相同特性的資料元素的有限序列。

線性表是一種在實際中廣泛使用的資料結構,常見的線性表:順序表、連結串列、棧、佇列、字串。

順序表

定義:

用一段實體地址連續的儲存單元依次儲存資料元素的線性結構(邏輯上連續,物理上也連續)

(1)靜態順序表:使用定長陣列儲存。

(2)動態順序表:使用動態開闢的陣列儲存

【注意】靜態順序表的定長陣列導致N定大了,空間開多了浪費,開少了不夠用,所以相比之下,動態陣列更為靈活,可根據需要動態分配空間大小

實現方法:

增、刪、改、查

增加操作:從頭部插入、從尾部插入、在任意索引位置處插入

刪除操作:根據索引刪除元素、根據元素值刪除第一個出現的該元素、根據元素值刪除所有該值元素 

查詢操作:根據元素值查詢是否存在某元素、根據索引下標返回該處元素值、根據元素值返回索引下標 

修改操作:根據索引下標修改該處元素 

程式碼實現:

public class MyArray {
    private int[]data;
    private int size;
//    無參構造
    public MyArray(){
        this.data=new int[5];
    }
//    有參構造
    public MyArray(int n){
        this.data=new int[n];
    }
//    grow方法用於擴充記憶體
    private void grow() {
        int[] newdata= Arrays.copyOf(data,size*2);
        this.data=newdata;
    }
//    toString方法輸出順序表內容
    public String toString(){
        String str="[";
        for (int i = 0; i <size ; i++) {
            str+=data[i];
            if(i!=size-1){
            str+=",";
            }
        }
        str+="]";
        return str;
    }
//    尾插法:
    public void addLast(int value){
        if(size== data.length){
            grow();
        }
        data[size]=value;
        size++;
    }
//    頭插法:
    public void addFirst(int value){
        if(size==data.length){
            grow();
        }
        for (int i = size-1; i >=0; i--) {
            data[i+1]=data[i];
        }
        data[0]=value;
        size++;
    }
//    中間任意位置插入:
    public void addIndex(int index,int value){
        if(size==data.length)
            grow();
        if(index<0||index>size){
            System.err.println("插入位置不合理!");
            return;
        }
        else {
            for (int i = size - 1; i >= index; i--) {
                data[i + 1] = data[i];
            }
            data[index] = value;
            size++;
        }
    }
//    檢視當前陣列中是否存在該元素
    public boolean contains(int value){
        for (int i = 0; i < size; i++) {
            if(data[i]==value)
                return true;
        }
        return false;
    }
//    查詢當前陣列元素對應的下標
    public int getIndex(int value){
        for (int i = 0; i < size; i++) {
            if(data[i]==value){
                return i;
            }
        }
        return -1;
 
    }
//    查詢陣列下標為index的元素
    public int getValue(int index) {
        if (index < 0 || index > size - 1) {
            System.out.println("輸入下標不合理");
            return -1;
        }
 
        return data[index];
    }
//    根據索引刪除元素,注意將最後一個元素置為0
    public void removeIndex(int index){
        if(index<0||index>=size){
            System.err.println("輸入不合法!");
        }
        for (int i = index; i <size-1; i++) {
            data[i]=data[i+1];
        }
        size--;
        data[size]=0;
    }
//    刪除第一個元素值為value的元素
    public void removeValueOnce(int value){
        int a=getIndex(value);
      removeIndex(a);
 
        }
//        刪除所有元素值為value的元素
    public void removeValueAll(int value){
        for (int i = 0; i < size; i++) {
           while(i!=size||data[i]==value)
               removeIndex(i);
 
        }
 
    }
//    根據索引修改元素
    public void recompose(int index,int newValue){
        if(index<0||index>=size){
            System.err.println("輸入不合法!");
        }
 
        data[index]=newValue;
    }
    }

連結串列

定義:

邏輯上連續,物理上非連續儲存。

連結串列由一個個節點組成,每個節點儲存該節點處的元素值val 以及下一個節點的地址next,由地址next實現邏輯上連續

分類:

分類方式:

(1)單連結串列、雙連結串列

(2)帶虛擬頭節點、不帶虛擬頭節點

(3)迴圈連結串列、非迴圈連結串列

按不同分類方式組合有8種:

非迴圈四種:

(1)不帶虛擬頭節點的單向連結串列(非迴圈)

(2)帶虛擬頭節點的單向連結串列(非迴圈)

(3)不帶虛擬頭節點的雙向連結串列(非迴圈)

(4)帶虛擬頭節點的雙向連結串列(非迴圈)

迴圈四種:

(5)不帶虛擬頭節點的單向連結串列(迴圈)

(6)帶虛擬頭節點的單向連結串列(迴圈)

(7)不帶虛擬頭節點的雙向連結串列(迴圈)

(8)帶虛擬頭節點的雙向連結串列(迴圈)

其中:

(1)不帶虛擬頭節點的單向連結串列(非迴圈):結構簡單,一般不會單獨用來存資料。實際中更多是作為其他資料結構的子結構,如雜湊桶、圖的鄰接表等等。這種結構在筆試面試中出現很多

(7)不帶虛擬頭節點的雙向連結串列(迴圈):在Java的集合框架庫中LinkedList底層實現就是無頭雙向迴圈連結串列

實現方法:

增、刪、查、改     和順序表實現方法基本一樣;

唯一注意:帶虛擬頭節點與不帶虛擬頭節點相比,帶虛擬頭節點避免了考慮頭節點為空的特殊情況

程式碼實現:

(1)不帶虛擬頭節點的單連結串列:

class Node {
//    val表示儲存的數值,next表示下一個節點的地址
    int val;
    Node next;
 
    //    構造方法
    public Node(int val) {
        this.val = val;
    }
}
 
public class SingleList {
    //    size表示節點個數
//    head表示頭節點
    private int size;
    private Node head;
 
    //定義toString方法以輸出連結串列內容
    public String toString() {
        String str = "";
        Node node = head;
        while (node != null) {
            str += node.val;
            str += "->";
            node = node.next;
        }
        str += null;
        return str;
    }
 
    //將判斷輸入的索引是否合法抽象為方法islegal
    public boolean islegal(int index) {
        if (index < 0 || index > size) {
            return false;
        } else {
            return true;
        }
    }
 
    //    頭插法
    public void addHead(int value) {
        Node node = new Node(value);
        if (head == null) {
            head = node;
 
        } else {
            node.next = head;
            head = node;
        }
        size++;
    }
 
    //    中間任意位置插入
    public void addIndex(int index, int val) {
        if (!islegal(index)) {
            System.out.println("輸入資料不合法!");
            return;
        }
        if (index == 0) {
            addHead(val);
            return;
        }
        Node node = new Node(val);
        Node pre = head;
 
        for (int i = 0; i < index - 1; i++) {
            pre = pre.next;
        }
        node.next = pre.next;
        pre.next = node;
        size++;
        return;
    }
 
    //    尾插法
    public void addLast(int val) {
        if (head == null) {
            addHead(val);
        } else {
            addIndex(size, val);
        }
    }
 
    //    刪除指定索引位置的元素
    public void removeIndex(int index) {
 
        if (islegal(index)) {
            if (index == 0) {
                Node temp = head;
                head = head.next;
                temp.next = null;
                size--;
                return;
            } else {
 
                Node pre = head;
                for (int i = 0; i < index - 1; i++) {
                    pre = pre.next;
                }
                Node cur = pre.next;
                pre.next = cur.next;
                cur.next = null;
 
                size--;
            }
 
        } else {
            System.out.println("輸入資料不合法!");
        }
    }
 
    //    根據元素值刪除元素,且只刪除第一次出現的該元素
    public void removeValueOnce(int val) {
        if (head.next != null && head.val == val) {
            removeIndex(0);
        } else {
            Node pre = head;
            while (pre.next != null) {
                if (pre.next.val == val) {
                    pre.next = pre.next.next;
                    pre.next.next = null;
                    size--;
                    return;
 
                }
                pre = pre.next;
            }
        }
    }
 
    //    根據元素值刪除元素,且刪除所有該元素
    public void removeValueAll(int val) {
        while (head.next != null && head.val == val) {
            Node temp = head;
            head = head.next;
            temp = null;
            size--;
        }
        if (head == null) {
            return;
        } else {
            Node pre = head;
            while (pre.next != null) {
                if (pre.next.val == val) {
                    pre.next = pre.next.next;
                    size--;
                    return;
 
                } else {
                    pre = pre.next;
                }
            }
        }
    }
 
    //       根據元素值刪除元素,且刪除所有該元素並返回頭節點(帶虛假節點)
    public Node removeValueAllWithDummy(int val) {
        Node dummyHead = new Node(-1);
        dummyHead.next = head;
        Node pre = dummyHead;
        while (pre.next != null) {
            if (pre.next.val == val) {
                Node cur = pre.next;
                pre.next = cur.next;
                cur.next = null;
                size--;
            } else {
                pre = pre.next;
            }
        }
        return dummyHead.next;
 
    }
 
    //    根據索引查元素值
    public int get(int index) {
        if (islegal(index)) {
            Node cur = head;
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            return cur.val;
        } else {
            System.out.println("輸入資料不合法!");
            return -1;
        }
    }
 
    //    能否查到給定的某元素值(自己寫的,好像很複雜)
    public boolean contains(int val) {
        boolean a = false;
        if (head == null) {
            System.out.println("該連結串列為空!");
            return false;
        } else {
            Node node = head;
 
            for (int i = 0; i < size; i++) {
                if (node.val == val) {
                    a = true;
                }
                node = node.next;
            }
        }
        return a;
    }
 
    //    能否查到給定的某元素值,老師寫的方法
    public boolean contains2(int val) {
        for (Node temp = head; temp != null; temp = temp.next) {
            if (temp.val == val) {
                return true;
            }
        }
        return false;
    }
 
    //    根據索引修改元素值
    public void set(int index, int newValue) {
        if (islegal(index)) {
            Node node = head;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            node.val = newValue;
            return;
        }
        System.out.println("輸入資料不合法!");
    }
}

(2)帶虛擬頭節點的單連結串列

以在指定索引位置插入元素為例,理解虛擬頭節點的作用即可

public class SingleListWithDummyHead {
    Node dummyNode=new Node(-1);
    int size;
//    在指定位置插入元素,因為有虛擬頭節點故不用考慮index=0的情況,全部為在中間位置插入的情況
    public void addIndex(int index,int value){
//        先判斷index是否合法
        if(index<0||index>size){
            System.out.println("illegal");
            return;
        }
        Node a=new Node(value);
        Node pre=dummyNode;
        for(int i=0;i<index;i++){
            pre=pre.next;
        }
        a.next=pre.next;
        pre.next=a;
        size++;
    }
}

(3)帶虛擬頭節點的雙連結串列

public class DoubleLinkedList {
    private int size;
    private Node dummyHead = new Node(-1);
    private Node tail;
 
    //    定義toString方法用以方便輸出
    public String toString() {
        String s = "";
        Node node = dummyHead.next;
        while (node != null) {
            s = s + node.val;
            s = s + "->";
            node = node.next;
        }
        s += "null";
        return s;
    }
 
    //    判斷輸入的索引是否合法
    private boolean isRange(int index) {
        if (index < 0 || index >= size) {
            System.out.println("輸入不合法!");
            return false;
        } else
            return true;
    }
 
    //    頭插法
    public void addHead(int val) {
        Node a = new Node(val, dummyHead, dummyHead.next);
//連結串列為空
        if (dummyHead.next == null) {
            tail = a;
            dummyHead.next = a;
        }
//        否則連結串列不為空
        else {
            dummyHead.next.prev = a;
            dummyHead.next = a;
        }
        size++;
    }
 
    //    尾插法
    public void addTail(int val) {
        Node a = new Node(val, tail, null);
//        連結串列為空
        if (dummyHead.next == null) {
            dummyHead.next = a;
        }
//        連結串列不為空
        else {
            tail.next = a;
        }
        tail = a;
        size++;
    }
 
    //    中間位置插入
    public void addMiddle(int index, int val) {
//      判斷插入位置合理否
        if (index < 0 || index > size) {
            System.out.println("輸入不合法!");
        }
//        頭部插入
        else if (index == 0) {
            addHead(val);
        }
//        尾部插入
        else if (index == size) {
            addTail(val);
        }
//        中間任意位置插入
        else {
//先找到要插入位置的前一個元素,可另一個方法找該元素
            Node a = new Node(val, find(index), find(index).next);
            find(index).next.prev = a;
            find(index).next = a;
            size++;
        }
    }
 
    //    這裡find的方法是找到index位置的前一個節點元素
    public Node find(int index) {
        Node f = dummyHead;
        for (int i = 0; i < index; i++) {
            f = f.next;
        }
        return f;
 
    }
 
    //    根據索引刪除指定位置的元素
    public void removeIndex(int index) {
        if (index < 0 || index >= size) {
            System.out.println("輸入不合法!");
            return;
        } else {
            find(index).next.next.prev = find(index);
            find(index).next = find(index).next.next;
            size--;
        }
    }
 
    //    刪除指定節點
    private void deleteNode(Node node) {
//        分治思想,先處理node與左邊節點,再處理node與右邊節點
        Node pre = node.prev;
        Node next = node.next;
//        處理左邊,因為有虛擬頭節點,故不用另討論為頭節點的情況
        pre.next = next;
        node.prev = null;
//       處理右邊
        if (next == null) {
            tail = pre;
        } else {
            next.prev = pre;
            node.next = null;
        }
        size--;
 
    }
 
    //    刪除指定元素值(只刪除第一次出現的)
    public void removeValueOnce(int val) {
        for (Node a = dummyHead.next; a != null; a = a.next) {
            if (a.val == val) {
                deleteNode(a);
                return;
            }
        }
        System.out.println("連結串列中無該元素故無法刪除");
 
    }
 
    public void removeValueAll(int val) {
        for (Node a = dummyHead.next; a != null; ) {
            if (a.val == val) {
                Node b = a.next;
                deleteNode(a);
                a = b;
            } else a = a.next;
        }
    }
 
    //    根據索引查詢元素值
    public int get(int index) {
        if (isRange(index)) {
            return find(index).next.val;
        } else {
            return -1;
        }
    }
 
    //    查詢是否存在某元素
    public boolean contains(int val) {
        Node a = dummyHead;
        while (a.next != null) {
            if (a.next.val == val) {
                return true;
            }
            a = a.next;
        }
        return false;
    }
 
    //    修改,將指定位置元素修改為另一值
    public void set(int index, int newValue) {
        if (isRange(index)) {
            find(index).next.val = newValue;
        }
 
    }
 
 
}
 
//節點類
class Node {
    int val;
    Node prev;
    Node next;
 
    public Node(int val) {
        this.val = val;
    }
 
    public Node(int val, Node prev, Node next) {
        this.val = val;
        this.prev = prev;
        this.next = next;
    }
}
 

順序表 & 連結串列

順序表:

優點:空間連續、支援隨機存取

缺點:中間或前面部分的插入刪除時間複雜度O(N)

           增容的代價比較大。

連結串列:

優點:任意位置插入刪除時間複雜度為O(1)

           沒有增容問題,插入一個開闢一個空間 

缺點:以節點為單位儲存,不支援隨機存取

總結

到此這篇關於Java實現順序表和連結串列結構的文章就介紹到這了,更多相關Java順序表和連結串列內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com