欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java數(shù)據(jù)結(jié)構(gòu)之順序表和鏈表精解

 更新時(shí)間:2021年09月26日 15:41:37   作者:愛內(nèi)卷的王同學(xué)  
我在學(xué)習(xí)完順序表后一直對(duì)順序表和鏈表的概念存在一些疑問,這里給出一些分析和看法,通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下

前言

兩個(gè)數(shù)據(jù)結(jié)構(gòu):順序表和鏈表

數(shù)據(jù)結(jié)構(gòu)是一門學(xué)科,和語言無關(guān)。

數(shù)據(jù) + 結(jié)構(gòu):一種描述和組織數(shù)據(jù)的方式。

1. 順序表

順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu),一般情況下采用數(shù)組存儲(chǔ)。在數(shù)組上完成數(shù)據(jù)的增刪查改。其邏輯上和物理上都是連續(xù)的。

問題引入:一個(gè)數(shù)組放在這,我們?nèi)绾尾拍茏约翰蝗?shù),讓程序自己進(jìn)行計(jì)數(shù)?

答:在引入變量,每次放一個(gè)元素就更新一次。(如下圖,為問題的示意)

hpMs1J.jpg

也就是說順序表的底層其實(shí)是一個(gè)數(shù)組,在 java 當(dāng)中順序表都是動(dòng)態(tài)的因?yàn)?java 當(dāng)中的new 其實(shí)就相當(dāng)于 c 語言中的 malloc 。

代碼實(shí)現(xiàn)

我們來實(shí)現(xiàn)一個(gè)動(dòng)態(tài)順序表,以下是需要支持的接口。 各個(gè)函數(shù)的具體實(shí)現(xiàn)部分要我們自己來寫。

public class MyArrayList {//此為創(chuàng)建一個(gè)接口
    public int[] elem;
    public int usedSize;
    public static int capacity = 10;

    public MyArrayList() {
        this.elem = new int[capacity];//新建一個(gè)數(shù)組
    }
    public boolean isFull() {
        return this.usedSize == capacity;//判斷數(shù)組是否已滿
    }

    // 在 pos 位置新增元素
    public void add(int pos, int data) { //pos為要插入元素的下標(biāo),data為要插入的數(shù)據(jù)
        if (pos < 0 || pos > this.usedSize) {
            System.out.println("pos位置不合法!");
            return;
        }
        //1、滿的情況   2、pos的合法情況
        if (isFull()) {
            //擴(kuò)容
            this.elem = Arrays.copyOf(this.elem, 2 * capacity);
            capacity *= 2;//新的容量
        }//此處調(diào)用前面 isfull() 函數(shù)來判斷是否已滿,返回真,執(zhí)行if里面的語句,擴(kuò)容兩倍;若不滿,返回假,跳過if內(nèi)的語句。
        for (int i = this.usedSize - 1; i >= pos; i--) {
            this.elem[i + 1] = this.elem[i];
        }//在前面的代碼確定數(shù)組里面有盈余位置的情況下,將前一個(gè)數(shù)賦給后一個(gè)位置,從而騰出 pos 位置
        this.elem[pos] = data; //給pos 位置的函數(shù)賦我們想要的值(即data)
        this.usedSize++;//usedsize 是奇數(shù)器,pos位置增加一個(gè)數(shù)組,自然要算上
    }

    // 打印順序表
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i] + " ");
        }
        System.out.println();
    }// 此處使用for循環(huán)遍歷打印

    public boolean isEmpty() {
        return this.usedSize == 0;
    }//判斷數(shù)組是否為空

    // 判定是否包含某個(gè)元素
    public boolean contains(int toFind) {
        if (isEmpty()) return false;
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elem[i] == toFind) {
                return true;
            }
        }
        return false;
    }//toFind 為我們要查找的元素,先判斷是否為空,再用循環(huán)遍歷判斷是否為該數(shù)

    // 查找某個(gè)元素對(duì)應(yīng)的位置
    public int search(int toFind) {
        if (isEmpty()) return -1;
        for (int i = 0; i < this.usedSize; i++) {
            if (this.elem[i] == toFind) {
                return i;
            }
        }
        return -1;
    }//這個(gè)查找和上面的那個(gè)判斷唯一的區(qū)別就是上面返回的是真與假,這個(gè)返回的是查到的那個(gè)數(shù),本質(zhì)的方法都是一樣的

    // 獲取 pos 位置的元素
    public int getPos(int pos) {
        if (isEmpty()) {
            //return -1; 業(yè)務(wù)的處理
            throw new RuntimeException("順序表是空的");//手動(dòng)拋出錯(cuò)誤(異常)
        }
        if (pos < 0 || pos >= this.usedSize) {
            throw new RuntimeException("pos不合法");//手動(dòng)拋出錯(cuò)誤(異常)
        }

        return this.elem[pos];
    }//這個(gè)其實(shí)很簡單,只需排除一下空表和pos 不合法的情況,之后返回 elem[pos]的值就行

    // 獲取順序表長度
    public int size() {
        return this.usedSize;
    }

    // 給 pos 位置的元素設(shè)為 value
    public void setPos(int pos, int value) {
        if (pos < 0 || pos >= this.usedSize) {
            System.out.println("pos不合法!");
            return;
        }
        if (isEmpty()) {
            System.out.println("順序表為空!");
            return;
        }
        this.elem[pos] = value;
    }//這個(gè)也簡單,只要判斷一下 數(shù)組是否不合法,是否為空,直接 this.elem[pos] = value就行了

    //刪除第一次出現(xiàn)的關(guān)鍵字key
    public void remove(int toRemove) {
        if (isEmpty()) return;
        int index = search(toRemove);
        if (index == -1) {
            System.out.println("沒有你要?jiǎng)h除的數(shù)字");
            return;
        }
        for (int i = index; i < this.usedSize - 1; i++) {
            this.elem[i] = this.elem[i + 1];
        }
        this.usedSize--;
    }
    //這里就是判斷數(shù)組是否為空,index 是否存在(此處調(diào)上面 search 函數(shù)),最后用for 循環(huán)遍歷,將后一個(gè)元素覆蓋在前一個(gè)元素上面

    // 清空順序表
    public void clear() {
        for (int i = 0; i < this.usedSize; i++) {
            this.elem[i] = 0;
        }
        this.usedSize = 0;
    }//用for循環(huán)遍歷每一個(gè)元素,將其賦值為 0 ,之后再將計(jì)數(shù)器 usedsized 也賦值為 0
}

順序表的寫起來是非常簡單的,但是他也是有一定的缺陷和不足的。它主要是負(fù)責(zé)實(shí)現(xiàn)增刪查改的功能,查改功能是很簡便的,如果直接就給定下標(biāo)的話,時(shí)間復(fù)雜度就是O(1),但是增刪的話,時(shí)間復(fù)雜度就必定為 O(N),這就非常麻煩(也就是說以后當(dāng)我們工作中要用查改就選用順序表就是最優(yōu)選的)。所以我們接下來就有必要引入鏈表這一種數(shù)據(jù)結(jié)構(gòu)。

2. 鏈表

鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)存儲(chǔ)結(jié)構(gòu),其存儲(chǔ)結(jié)構(gòu)便是上面放值,下面放下一個(gè)節(jié)點(diǎn)的地址,也就是說,雖然他是不連續(xù)的,當(dāng)上一個(gè)節(jié)點(diǎn)仍然能找到下一個(gè)節(jié)點(diǎn),類似于鏈子一樣,一個(gè)串一個(gè),但區(qū)別是,鏈表彼此之間不相接觸。

鏈表圖解

hGYWbq.jpg

代碼實(shí)現(xiàn)

class Node {//創(chuàng)建一個(gè)節(jié)點(diǎn)類
//一個(gè)節(jié)點(diǎn)是有兩個(gè)域或者多個(gè)域的,以下便是創(chuàng)建的兩個(gè)域
    public int val;//鏈表里面的數(shù)值
    public Node next;//這是一個(gè)引用變量,用于標(biāo)識(shí)每個(gè)鏈表單元的下一個(gè)數(shù)的地址,因?yàn)樗娴氖枪?jié)點(diǎn),而節(jié)點(diǎn)的類型就是Node,所以我們也用Node 來定義這個(gè)變量。

    public Node(int val) { //這是一個(gè)類里面的一個(gè)方法用來給鏈表當(dāng)中的val賦值
        this.val = val;//因?yàn)閚ext存的hi下一個(gè)節(jié)點(diǎn)的地址,所以我們是不知道也不能賦值的
        }
}
//鏈表
public class MyLinkedList {//此處為創(chuàng)建鏈表的接口
     public Node head;//標(biāo)識(shí)單鏈表的頭節(jié)點(diǎn)(這也是一個(gè)引用變量)

    /**
     * 窮舉的方式 創(chuàng)建鏈表  當(dāng)然很low。此處只是為了好理解
     */
    public void createList() {
        Node node1 = new Node(12);//新建一個(gè)節(jié)點(diǎn)的對(duì)象node1,此為節(jié)點(diǎn)1,賦值為12
        Node node2 = new Node(3);//此為節(jié)點(diǎn)2,賦值3
        Node node3 = new Node(5);//此為節(jié)點(diǎn)3,賦值5
        Node node4 = new Node(2);//此為節(jié)點(diǎn)4,賦值6
        node1.next = node2;//node1中的next存node2(node2是一個(gè)對(duì)象的引用,存的是對(duì)象在堆中的地址)

        // 也就是說node1.next指向node2指向的地址

        node2.next = node3;//以下三個(gè)原理同上
        node3.next = node4;
        this.head = node1;//此處為定義一個(gè)head(后面head可能會(huì)有改動(dòng))

    }

    /**
     * 打印單鏈表
     */
    public void show() {
        Node cur = this.head;//將head的值暫時(shí)存到cur中,這樣就是單純地是cur在變,head值不改變
        while(cur != null) {
            System.out.print(cur.val+" ");
            cur = cur.next;//通過這道程序?qū)ur依次向后移動(dòng)最后到空的時(shí)候打印出來
        }
        System.out.println();
    }

    //得到單鏈表的長度
    public int size() {
        Node cur = this.head;//同樣地,此處還以cur為介質(zhì)向后移動(dòng)
        int count = 0;
        while (cur != null) {
            count++;//4  cur 依次經(jīng)過各個(gè)節(jié)點(diǎn)的時(shí)候count便隨之計(jì)數(shù)
            cur = cur.next;
        }
        return count;
    }

    //查找是否包含關(guān)鍵字key是否在單鏈表當(dāng)中 15
    public boolean contains(int val) {//這里函數(shù)參數(shù)中的val就是我們要查找的 key
        Node cur = this.head;
        while (cur != null) {//同樣的方法遍歷節(jié)點(diǎn)
            if(cur.val == val) {
                return true;
            }
            cur = cur.next;
        }
        return false;//如果遍歷完了都沒有找到那就肯定沒有了
    }



    //頭插法
    public void addFirst(int data) {//date為要插入的數(shù)據(jù)
        Node node = new Node(data);//此處為創(chuàng)建要插入的節(jié)點(diǎn)(對(duì)象)內(nèi)部存儲(chǔ)的值為 date
        if(this.head == null) {//判斷頭結(jié)點(diǎn)是否為空
            this.head = node;// 若為空,則根本沒有鏈表,直接將要插入的節(jié)點(diǎn)賦給head
        }else { //此為有鏈表存在的狀況
            node.next = this.head;//此處操作讓head原本指向的對(duì)象(節(jié)點(diǎn))成為鏈表中的第二個(gè)節(jié)點(diǎn)
            this.head = node;
            //上面的操作讓head指向了node指向的節(jié)點(diǎn)(head是頭結(jié)點(diǎn)的標(biāo)志,自然要讓他移到第一位)
        }
    }


    //尾插法
    public void addLast(int data) {//data為要插入的數(shù)據(jù)
        Node node = new Node(data);//新建一個(gè)節(jié)點(diǎn)改值為data
        if(this.head == null) {//判斷鏈表是否為空,若為空,則為一個(gè)節(jié)點(diǎn)便也是最后一個(gè)
            this.head = node;//直接讓head指向node指向的節(jié)點(diǎn)即可
        }else {//若節(jié)點(diǎn)不為空時(shí)
            Node cur = this.head;//將head中的地址賦給中間變量cur
            while (cur.next != null) {
                cur = cur.next; //用cur遍歷節(jié)點(diǎn)
            }
            cur.next = node;//這是的cur已經(jīng)待在了最后一個(gè)節(jié)點(diǎn)上它的next上沒有東西了
                            //所以這個(gè)時(shí)候?qū)ode指向的地址交給next,既完成了節(jié)點(diǎn)的插入
        }
    }


    public Node searchPrev(int index) {
        Node cur = this.head;//同樣地,以 cur為介質(zhì)
        int count = 0;
        while(count != index-1) {
            cur = cur.next;//用cur 遍歷鏈表
            count++;
        }
        return cur;//此時(shí)返回的 cur剛好指向 index-1這個(gè)節(jié)點(diǎn)
    }

    //任意位置插入,第一個(gè)數(shù)據(jù)節(jié)點(diǎn)為0號(hào)下標(biāo)
    public void addIndex(int index,int data) {
        if(index < 0 || index > size()) {//判斷index是否合法
            System.out.println("下標(biāo)不合法");
            return;
        }
        //頭插法
        if(index == 0) {//index為0時(shí),就是頭插法
            addFirst(data);
            return;
        }
        //尾插法
        if(index == size()) {//index為數(shù)組長度是就是尾插法
            addLast(data);
            return;
        }
        Node cur = searchPrev(index);//讓cur 拿到第index-1位置節(jié)點(diǎn)地址
        Node node = new Node(data);//創(chuàng)建一個(gè)存了數(shù)據(jù)為data 的節(jié)點(diǎn)
        node.next = cur.next;//將剛剛創(chuàng)建的節(jié)點(diǎn)中的next存進(jìn) cur 中的next(即把index的地址放到node的next里)
        cur.next = node;//再將node中存著的地址放到cur的next中
        //插中間歸根到底就是next的交換,以下為傳遞順序:
        //index-1中index的地址 ——> node.next
        //node的地址 ——> cur.next
    }

    //下面這段代碼用來找val的前驅(qū)節(jié)點(diǎn)
    public Node searchPrevNode(int val) {
        Node cur = this.head;//還是以 cur 為中間量
        while (cur.next != null) {//cur 遍歷至數(shù)組的最后一位
            if(cur.next.val == val) {//這里是判斷cur的下一個(gè)節(jié)點(diǎn)中的值是否為val
                return cur;//如果是的就返回cur
            }
            cur = cur.next;
        }
        return null;
    }

    //刪除第一次出現(xiàn)關(guān)鍵字為key的節(jié)點(diǎn)
    public void remove(int val) {
        if(this.head == null) return;

        //單獨(dú)判斷頭節(jié)點(diǎn)的問題
        if(this.head.val == val) {
            this.head = this.head.next;
            return;//這段代碼的意思就是如果就是如果第一個(gè)節(jié)點(diǎn)中的值就是要?jiǎng)h除的值
            //那么直接就讓第一個(gè)節(jié)點(diǎn)的引用指向下一個(gè)節(jié)點(diǎn),其實(shí)就是忽略第一個(gè)起到了一個(gè)刪除效果
            //而原來第一個(gè)節(jié)點(diǎn)變成了沒有人引用的對(duì)象會(huì)被jvm回收
        }
        Node cur = searchPrevNode(val);//拿到下一個(gè)節(jié)點(diǎn)值為val的cur
        if(cur == null) {//
            System.out.println("沒有你要?jiǎng)h除的節(jié)點(diǎn)!");
            return;
        }
        //下面就是cur 存在的情況
        Node del = cur.next;//創(chuàng)建一個(gè)節(jié)點(diǎn)del讓其使用cur中下一個(gè)節(jié)點(diǎn)的地址(也就是要?jiǎng)h的val地址)
        cur.next = del.next;//再把del中的存的下一個(gè)地址賦給 cur,那么就間接地抹去了val
    }
    //刪除所有值為key的節(jié)點(diǎn)
    public void removeAllKey(int val) {
        if(this.head == null) {//節(jié)點(diǎn)是否為空?
            return;
        }
        Node prev = this.head;//讓 prev指向 head指向的對(duì)象(prev本質(zhì)上就是前驅(qū)節(jié)點(diǎn))
        Node cur = this.head.next;//讓 cur指向 head.next位置
        while (cur != null) { //用cur 來遍歷數(shù)組
            if(cur.val == val) { //
                prev.next = cur.next;//抹去要?jiǎng)h的節(jié)點(diǎn)
                cur = cur.next;//移動(dòng)至下一個(gè)節(jié)點(diǎn)處
            }else {//以下是 cur處的值 不等于 val 的情況
                prev = cur;//將prev移到cur的位置
                cur = cur.next;//再將cur 向后移動(dòng)
            }
        }
        //只有頭結(jié)點(diǎn)且頭結(jié)點(diǎn)便是要?jiǎng)h除的val的情況
        if(this.head.val == val) {
            this.head = this.head.next;
        }
    }
    public void clear(){

    }

}

到此這篇關(guān)于Java數(shù)據(jù)結(jié)構(gòu)之順序表和鏈表精解的文章就介紹到這了,更多相關(guān)Java 順序表和鏈表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring boot整合shiro安全框架過程解析

    spring boot整合shiro安全框架過程解析

    這篇文章主要介紹了spring boot整合shiro安全框架過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java實(shí)現(xiàn)簡單圖形界面計(jì)算器

    Java實(shí)現(xiàn)簡單圖形界面計(jì)算器

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單圖形界面計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • JVM完全解讀之Metaspace解密源碼分析

    JVM完全解讀之Metaspace解密源碼分析

    通過這篇文章,你將可以了解到,為什么會(huì)有metaspace?metaspace的組成,metaspace的VM參數(shù),jstat里我們應(yīng)該關(guān)注metaspace的哪些值,有需要的朋友可以借鑒參考下
    2022-01-01
  • 使用lombok@Data存在extends時(shí)需要注意的問題

    使用lombok@Data存在extends時(shí)需要注意的問題

    在Java編程中,正確實(shí)現(xiàn)equals方法是保證對(duì)象比較一致性的關(guān)鍵,使用instanceof檢查類型可能導(dǎo)致違反對(duì)稱性原則,即當(dāng)子類和父類都重寫equals時(shí)可能出現(xiàn)a.equals(b)不等于b.equals(a)的情況,Lombok的@EqualsAndHashCode注解可以通過callSuper=true參數(shù)
    2024-10-10
  • 解決mybatis case when 報(bào)錯(cuò)的問題

    解決mybatis case when 報(bào)錯(cuò)的問題

    這篇文章主要介紹了解決mybatis case when 報(bào)錯(cuò)的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java基于分治算法實(shí)現(xiàn)的線性時(shí)間選擇操作示例

    Java基于分治算法實(shí)現(xiàn)的線性時(shí)間選擇操作示例

    這篇文章主要介紹了Java基于分治算法實(shí)現(xiàn)的線性時(shí)間選擇操作,涉及java排序、比較、計(jì)算等相關(guān)操作技巧,需要的朋友可以參考下
    2017-11-11
  • Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(41)

    Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(41)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07
  • Java優(yōu)先隊(duì)列?priority?queue

    Java優(yōu)先隊(duì)列?priority?queue

    本文主要介紹了Java優(yōu)先隊(duì)列?priority?queue,優(yōu)先隊(duì)列是一種特殊的數(shù)據(jù)結(jié)構(gòu)隊(duì)列中每一個(gè)元素都被分配到一個(gè)優(yōu)先權(quán)值,出隊(duì)順序按照優(yōu)先權(quán)值來劃分。一般有兩種出隊(duì)順序高優(yōu)先權(quán)出隊(duì)或低優(yōu)先權(quán)出隊(duì),想了解具體內(nèi)容的小伙伴可以參考下文內(nèi)容,希望對(duì)你有所幫助
    2021-12-12
  • transactionAttributes各屬性意義及配置

    transactionAttributes各屬性意義及配置

    這篇文章主要介紹了transactionAttributes各屬性意義及配置,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • PowerJob的CleanService清理服務(wù)流程

    PowerJob的CleanService清理服務(wù)流程

    這篇文章主要為大家介紹了PowerJob的CleanService清理服務(wù)流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2024-02-02

最新評(píng)論