Java 顺序表 链表(单链表 双链表)--- 学习总结 和 几个基本方法

顺序表和链表

1.线性表:是n个具有相同特性的数据元素的有限序列。

线性表是一种在实际中广泛使用的数据结构。
常见线性表:顺序表、链表、栈、队列、字符串…
线性表 逻辑上线性结构,物理结构上并不一定是连续的。
线性表在物理存储时,通常以数组和链式结构的形式存储。

在这里插入图片描述


2.顺序表:用一段物理地址连续的存储单元依次存储数据元素的线性结构。

依靠数组实现的一种数据结构。
一般采用数组存储,在数组上操作增删改查。
顺序表:物理上 逻辑上都连续
顺序表分为:👇
静态顺序表:使用定长数组存储 已知数据量。
动态顺序表:使用动态开辟的数组存储 灵活动态分配空间。

动态顺序表所需要的接口:

public class SeqList {
    // 打印顺序表
    public void display() {   }
    // 在 pos 位置新增元素
    public void add(int pos, int data) { }
    // 判定是否包含某个元素
    public boolean contains(int toFind) { return true; }
    // 查找某个元素对应的位置
    public int search(int toFind) { return -1; }
    // 获取 pos 位置的元素
    public int getPos(int pos) { return -1; }
    // 给 pos 位置的元素设为 value
    public void setPos(int pos, int value) {   }
    //删除第一次出现的关键字key
    public void remove(int toRemove) {   }
    // 获取顺序表长度
    public int size() { return 0; }
    // 清空顺序表
    public void clear() {   }
}

3.链表

链表:物理上不一定连续的,逻辑上一定是连续的

在这里插入图片描述
逻辑顺序通过引用链接

8种结构:单向、双向;带头、不带头;循环、非循环
带头:头节点作用标志头

1.单链表、双向链表:
在这里插入图片描述

2.不带头单链表、带头链表
在这里插入图片描述

3.单链表、循环单链表
在这里插入图片描述


重点:无头 单向非循环链表&无头 双向链表

无头 单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
在这里插入图片描述
无头 双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。
在这里插入图片描述

链表的实现

1、无头单向非循环链表实现 ---- 具体方法实现代码在文末

public class SingleLinkedList {
    //1.头插法
    public void addFirst(int data);
    
    //2.尾插法
    public void addLast(int data);
    
    //3.任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index,int data);
   public int getLength() {            //节点长度 及节点个数
   private ListNode searchIndex(int index) {  //找到index-1位置的节点
   
    //4.查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    
    //5.删除第一次出现关键字为key的节点
    public void remove(int key);
    private ListNode suchPrev(int key) {        //找到删除节点的前驱节点
    
    //6.删除所有值为key的节点
    public void removeAllKey(int key);
    
    //7.输出链表
    public void display();
    //8.清空链表
    public void clear();
    //9.链表长度 sout getLength返回值
    public int size();
    
}

2、无头双向链表实现 ---- 具体方法实现代码在文末

public class DoubleLinkedList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public boolean addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    public void display();
    public void clear();
}

1、无头单向非循环链表实现👇

import org.omg.CORBA.DATA_CONVERSION;

class ListNode {                      //模板节点 - 类
    public int data;                 //值,下个节点的地址
    public ListNode next;

    public ListNode(int data) {      //构造函数  参数节点的值
        this.data = data;
        this.next = null;
    }
}

class SingleLinkedList {         //无头单向非循环链表

    public ListNode head;       //默认为 null


    //1.头插法   不带头
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;          //第一次插入
        } else {
            node.next = head;         //node的下一个节点指向head地址
            head = node;              //将head地址变更为node地址
        }
    }


    //2.尾插法
    public void addLast(int data) {
        ListNode cur = this.head;
        ListNode node = new ListNode(data);
        if (head == null) {
            this.head = node;      //第一次插入
        } else {
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }

    }


    //3.插入到index位置  位置号从0开始记 插入位置有getlength+1个
    // getlength为节点个数    index=0;头插   index=getlength;尾插
    public void addIndex(int index,int data){        //插入位置,插入值

        if(index < 0 || index > getLength()) {  //判断插入位置是否合法
            System.out.print("位置不合法"+"  ");
        }
        else if(index == 0) {             //头插法
            addFirst(data);
        }
        else{
            ListNode prev = searchIndex(index);     //找插入点的 前后
            ListNode node = new ListNode(data);     //new一个节点
            node.next = prev.next;      //新节点next指向 插入点后的next  与插入点后拼接
            prev.next = node;           //插入点的next指向新节点地址   与插入点前拼接
        }

    }
    public int getLength() {            //节点长度 及节点个数
        int count = 0;
        ListNode cur = this.head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    private ListNode searchIndex(int index) {        //找到index-1位置的节点
        //prev-->index-1;                               返回当前节点的引用
        ListNode prev = this.head;
        int count = 0;
        while (count < index-1) {
            prev = prev.next;
            count++;
        }
        return prev;
    }


    //4.查找是否包含关键字key
    public boolean contains(int key){
        ListNode cur=this.head;
        while (cur!=null){
            if(cur.data==key){
                System.out.println("key值地址"+cur);        //找到key值,输出了地址
                return true;
            }
            cur=cur.next;
        }
        return false;
    }


    //5.删除第一次出现关键字为key的节点
    public void remove(int key){
        if(this.head == null) {     //判断链表是否为空
            return;
        }

        if(this.head.data==key){              //头节点值为key时
            this.head=head.next;              //删除头节点
        }else{
            if(searchPrev(key)==null){        //找不到删除节点  即前驱节点为空时
                System.out.println("没有值为"+key+"的节点,无法删除。");
                return;
            }
            ListNode prev=searchPrev(key);          //创建节点,为搜索前驱节点的返回值
            ListNode del=prev.next;              //del节点为prev的next
            prev.next=del.next;             //删除。前驱指向=删除节点指向  拼接删除了del节点
        }
    }
    private ListNode searchPrev(int key) {        //找到删除节点的前驱 返回值为节点类型
        ListNode prev = this.head;

        while (prev.next!=null){      //删除节点的值不等于key时,删除节点向后移
            if(prev.next.data==key){
                return prev;
            }
            prev=prev.next;
        }
        return null;
    }





    //6.删除值为key的所有节点        返回头节点地址
    public ListNode removeAllKey(int key){
        if(head==null){     //若一个节点都没有
            return null;    //直接return 空
        }

        ListNode prev = this.head;
        ListNode cur = this.head.next;      //cur 在prev后一个节点

        while (cur!=null){
            if(cur.data==key){              //cur值为key
                prev.next=cur.next;         //prev指向  cur.next  后拼接
                cur=cur.next;               //cur向后移一个节点
            }else{
                prev=prev.next;           //值不为key时
                cur=cur.next;       //prev  cur  后移 继续循环
            }
        }

        if(this.head.data==key){         //头节点等于key 删除 最后处理
            this.head=this.head.next;
        }
        return head;        //返回头节点地址
    }


    //7.输出链表
    public void display() {
        if (this.head == null) {
            return;
        } else {
            ListNode cur = this.head;
            while (cur != null) {
                System.out.print(cur.data + " ");
                cur = cur.next;
            }
            System.out.println();
        }
        System.out.println();
    }

    //8.清空单链表
    public void clear(){
        while(this.head.next!=null){
            ListNode cur=this.head.next;
            this.head.next=cur.next;
        }
        this.head=null;
    }
    
    //9.链表长度  sout getLength返回值
    public void size(){
        System.out.println("该链表长度:"+getLength());
    }
}

2、无头双向链表实现👇

class ListNode{              //节点
    public int data;
    public ListNode next;
    public ListNode prev;
    public ListNode(int data){
        this.data=data;
    }
}

class DoubleList {
    public ListNode head = null;            //头
    public ListNode last = null;            //尾巴  每插入一次标志一次


    //1.头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.last = node;
        } else {
            this.head.prev = node;
            node.next = this.head;
            this.head = node;         //头前移
        }
    }

    //2.尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (this.last == null) {
            this.head = node;
            this.last = node;
        } else {
            this.last.next = node;
            node.prev = this.last;
            this.last = node;         //尾巴后移
        }
    }

    //3.任意位置插入,第一个数据节点为0号下标
    public  void addIndex(int index,int data){
        if (index==0){
            addFirst(data);
            return ;
        }else if(index==size()){
            addLast(data);
            return ;
        }

        ListNode node=new ListNode(data);
        ListNode cur=serchIndex(index);     //cur为要插入的位置节点
        if(cur == null) {
            return ;
        }
        node.next = cur;
        node.prev = cur.prev;
        cur.prev.next = node;       //先改prev.next,
        cur.prev = node;            //后改prev。  否则prev.next为node.next
    }

    //a.输出显示
    public void display() {
        ListNode cur=this.head;
        while(cur!=null){
            System.out.print(cur.data+" ");
            cur=cur.next;
        }
        System.out.println(  );
    }

    //b.找到插入位置
    private ListNode serchIndex(int index){
        if(index < 0 || index >= size()) {
            return null;
        }

        ListNode cur=this.head;
        while (index>0){
            cur=cur.next;
            index--;
        }
        return cur;
    }

    //c.得到单链表的长度
    public int size(){
        ListNode cur=this.head;
        int length=0;
        while (cur!=null){
            length++;
            cur=cur.next;
        }
        return length;
    }

//-----------------------------------

    //4.查找是否包含关键字key是否在双向链表当中
    public boolean contains(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if(cur.data==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

    //5.删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if(cur.data==key) {             //3种情况
                if (cur == this.head) {     //在头时
                    this.head = this.head.next;
                    this.head.prev = null;
                } else {                    //尾巴和中间
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {          //尾巴时
                        this.last = cur.prev;
                    }
                    return ;            //直接结束,删除第一个
                }
            }
             cur=cur.next;
        }
    }

    //6.删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur=this.head;
        while(cur!=null){
            if(cur.data==key) {             //3种情况
                if (cur == this.head) {     //在头时
                    this.head = this.head.next;
                    this.head.prev = null;
                } else {                    //尾巴和中间
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {          //尾巴时
                        this.last = cur.prev;
                    }
                    //不return 全部删除
                }
            }
            cur=cur.next;
        }
    }

    //7.删除全部
    public void clear(){
        while(this.head!=null){
            ListNode cur=this.head.next;        //cur始终为head.next
            this.head.next=null;
            this.head.prev=null;
            this.head=cur;
        }
        this.last=null;     //last 也要处理
    }
    //查看清空方法:
    // 1.cmd     2.jps(查看进程号) 3.jmap -histo:live 14272>e:log.txt    4.打开log.txt


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyril-zxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值