数据结构速学笔记: 单链表增删改查

目录

  1. 单链表的存储结构
  2. 单链表:增
  3. 单链表:删
  4. 单链表:改
  5. 单链表:查
  6. 本文示例代码

单链表的存储结构

简介
  1. 单链表是一种链式存取的数据结构,是实现线性表方式之一
  2. 链表都包含:元素(data域)+指针(next域)
  public static class LinkNode {
        //data域 start  --------
        int num;
        String name;
        String nickName;
        //data域 end  --------
        LinkNode next; // next域
        
		//....     
    }

  1. 链表的存储地址有可能连续,也有可能非连续

链表的存储结构:
在这里插入图片描述
例:单链表存储方式:

   head头节点.next  ->  A.next -> B.next ->C.next -> D.next

也有可能:

   head头节点.next  ->  A.next -> C.next ->B.next -> D.next

链表虽说是线性表的一种,存储结构也是链式的,但是链表的实际存储过程中,有可能并非是连续存储的地址。


本文示例的结构组成
示例代码结构组成

public class LinkList {
	// 表示头节点
    LinkNode node;

    public LinkList() {
        node = new LinkNode(0, "", "");
    }
    /**
     * 只有一种情况,add到链表尾部
     */
    public void add(LinkNode addnode) {    
    }
    /**
     * 按照顺序插入节点,形成一个有序的链表
     */
    public void addNum(LinkNode newNode) {  
    }
    /**
     * 修改
     * @param num 对应链表中元素的num
     *            场景:根据此num,修改对应链表中数据的nickName
     */
    public void update(int num, LinkNode upNode) { 
    }
	// 删除
    public void del(int num) {   
    }
	// 展示(遍历链表打印出来)
    public void show() {
    }

    /**
     * 链表内存结构:
     * 内存地址                   data域                    next域
     * next引用时指向的地址       当前节点存放的数据           用于指向下一个数据的内存地址
     */
    public static class LinkNode {// 链表中的元素
        int num; //示例含义: 排名
        String name; // 示例含义: 名字
        String nickName; //示例含义: 名字(艺名)
        LinkNode next; // next域(指针)

        public LinkNode(int num, String name, String nickName) {
            this.num = num;
            this.name = name;
            this.nickName = nickName;
        }
		// .....
    }
}
为了方便文章讲解,暂定一种链表

因为文字描述比较绕
这里暂定一种链表:
用A、B、C、D 表示原链表中的4个元素(关系:头节点 -> A.next -> B.next -> C.next- > D)E表示当前要插入的元素
A、B、C、D 、E都是LinkNode


增的方式有两种情况:1. 一种是一直添加在链表最后2. 另外一种是根据指定的条件进行有序的插入节点

1. 添加在链表最后

核心思路

  1. 遍历链表找到最后一位元素
  2. 用最后一位元素的next域指向要插入的数据
  3. 遍历过程中,通过LinkNode = LinkNode.next 实现链表后移一位

代码实现


// 头节点  :目的为了方便统一操作,头节点里的next也可以称为头指针
LinkNode node = new LinkNode(0, "", "");

 public void add(LinkNode addnode) {
        LinkNode n = node;
        while (true) {
            if (n.next == null) {//说明找到最后一个节点
                break;
            }
            //链表向后移一位,继续查找
            n = n.next;
        }
        //上面的循环会一直找到,n.next为null的情况,说明对应的node是最后一个
        // 将最后一个node数据的next指向要添加的node
        n.next = addnode;
    }

图文分析:
在这里插入图片描述

2. 按照顺序节点插入

场景:
这一种插入,会根据用户所期望的某个条件进入节点插入。
例如:根据num(排名)这个字段进行节点插入

如:1 2 3 4 这样的插入,按照数字序号顺序

核心思路

  • 遍历链表

    1. 链表为空,直接结束循环
    2. 当前链表位置是A,用A.next指向的Bnum,和当前要插入的Enum比较大小,如果Bnum(即:A.next指向的B)大于插入的Enum,则说明当前要插入E应该插入在B的前面(后面也有图文解释),结束循环
    3. 要插入的E,在链表中存在,结束循环(本示例不作处理,只做个打印)
    4. 没有满足上述三个条件的情况:通过LinkNode = LinkNode.next实现链表后移一位
  • 插入链表

    1. 需要断开A.next指向的B
    2. E.next指向B
    3. 然后A.next指向E
 E.next = A.next;// A.next 就是B
 A.next = E;

代码实现

 /**
     * 按照顺序插入节点,形成一个有序的链表
     */
    public void addNum(LinkNode newNode) {
        LinkNode n = node; //头节点
        boolean isFind = false;
        while (true) {
            if (n.next == null) break; // 分析 1-->说明当前没有元素
            if (n.next.num > newNode.num) {  // 分析 2-->说明当前newNode是在n.next前面
                break;
            } else if (n.next.num == newNode.num) {
                isFind = true;
                //分析 3-->要添加的数据已存在
                break;
            }
            //分析4--> 上述三个条件都不满足,向后位移,继续查找遍历
            n = n.next;
        }
        if (isFind) {
            System.out.println("要添加的数据已存在" + newNode);
        } else {
            // 分析5--> 插入链表
            newNode.next = n.next;
            n.next = newNode;
        }
    }

图文分析

分析2 -->
分析5 -->

在这里插入图片描述
最终实现:
在这里插入图片描述
这样最终就实现了按照顺序节点插入。


根据元素内的num字段进行删除
思路

  • 遍历链表

    1. 链表为空结束循环
    2. A.next即(B)的num和当前要删除的元素的num做对比,相等的话,表示 B就是要删除的元素
    3. 没有满足上述二个条件的情况:通过LinkNode = LinkNode.next实现链表后移一位
  • 删除元素

    1. 请看分析4-->

代码实现


    public void del(int num) {
        LinkNode n = node; // 表示头节点
        while (true) {
            if (n.next == null) break; //分析1--> 链表为空结束循环
            if (n.next.num == num) { // 分析2--> 匹配到要删除的num的对应元素
            /**
            *分析4--> 例:A、B、C 、D 4个元素,假如C是要删除的元素
            *         B.next.num(即C.num) == num(要删除的num)
            *         B.next= B.next.next;//这一步就是删除C的操作
            *         上一行解释:B.next即是C,B.next.next即是D,所以这里的意思B.next直接指向了D;
            *         B.next= B.next.next;相当于: B.next= D
            *         这里C就变成一个没有被引用的对象,最后会被GC回收
            */
                n.next = n.next.next; 
                break;
            }
            n = n.next; // 分析3--> 分析1、2 不满足条件,链表向后位移一位
        }
    }

图文分析

原链表:
在这里插入图片描述
移除C后链表:
在这里插入图片描述
C元素就处于游离状态,没有被引用了,最后会被GC回收掉。



    /**
     * @param num 对应链表中元素的num
     *            场景:根据此num,修改对应链表中数据的nickName
     */
    public void update(int num, LinkNode upNode) {
        LinkNode n = node; // 头节点
        while (true) {
            if (n.next == null) break; // 分析1-->链表为空
            if (n.next.num == upNode.num) { // 分析2 -->
                n.next.nickName = upNode.nickName; // 分析4 --> 找到要修改的元素,直接修改即可
                break;
            }
            n = n.next;// 分析3 --> 未找到对应num的元素,链表向后位移一位
        }
    }

 
    public LinkNode query(int num) {
        LinkNode n = node;
        LinkNode returnNode = null;
        while (true) {
            if (n.next == null) break;
            if (n.next.num == num) {
                returnNode = n.next; // 找到匹配的赋值给returnNode,最终返回  
                break;
            }
            n = n.next;
        }
        return returnNode;
    }

后续会总结一篇针对单链表的面试题。

欢迎大佬们指出不足,加以改进。Thanks!

如果对你有帮助,那就点个👍吧

本文示例代码


public class LinkList {

    LinkNode node;

    public LinkList() {
        node = new LinkNode(0, "", "");
    }

    /**
     * 只有一种情况,add到链表尾部
     */
    public void add(LinkNode addnode) {
        /**
         * 需要遍历整个列表
         * */
        LinkNode n = node;
        while (true) {
            if (n.next == null) {
                //说明找到最后一个节点
                break;
            }
            // 将下一个Node赋值给当前node,即:链表向后移一位,继续遍历
            n = n.next;
        }
        //  上面的循环会一直找到,n.next为null的情况,说明对应的node是最后一个
        // 将最后一个node数据的next指向要添加的node
        n.next = addnode;
    }


    /**
     * 按照顺序插入节点,形成一个有序的链表
     */
    public void addNum(LinkNode newNode) {
        LinkNode n = node;
        boolean isFind = false;
        while (true) {
            if (n.next == null) break;

            if (n.next.num > newNode.num) { // 说明当前newNode是在n.next前面
                break;
            } else if (n.next.num == newNode.num) {
                isFind = true;
                // 要添加的数据已存在
                break;
            }
            n = n.next;
        }
        if (isFind) {
            System.out.println("要添加的数据已存在" + newNode);
        } else {
            //插入链表
            newNode.next = n.next;
            n.next = newNode;

        }
    }

    /**
     * @param num 对应链表中元素的num
     *            场景:根据此num,修改对应链表中数据的nickName
     */
    public void update(int num, LinkNode upNode) {
        LinkNode n = node;
        while (true) {
            if (n.next == null) break;
            if (n.next.num == upNode.num) {
                n.next.nickName = upNode.nickName;
                break;
            }
            n = n.next;
        }
    }

    public void del(int num) {
        LinkNode n = node;
        while (true) {
            if (n.next == null) break;
            if (n.next.num == num) {
                n.next = n.next.next;
                break;
            }
            n = n.next;
        }
    }

    public void show() {
        if (node.next == null) {
            System.out.println("链表为null");
            return;
        }
        // 走到这里,说明链表至少存在一个数据
        LinkNode l = node.next;
        while (true) {
            if (l == null) {
                // 说明当前只有一个数据
                break;
            }
            System.out.println(l);
            l = l.next;// 链表后移
        }
    }

    public LinkNode query(int num) {
        LinkNode n = node;
        LinkNode returnNode = null;
        while (true) {
            if (n.next == null) break;
            if (n.next.num == num) {
                returnNode = n.next;
                break;
            }
            n = n.next;
        }
        return returnNode;
    }
    /**
     * 链表内存结构:
     * 内存地址                   data域                    next域
     * next引用时指向的地址       当前节点存放的数据           用于指向下一个数据的内存地址
     */
    public static class LinkNode {
        int num;
        String name;
        String nickName;
        LinkNode next;

        public LinkNode(int num, String name, String nickName) {
            this.num = num;
            this.name = name;
            this.nickName = nickName;
        }

        @Override
        public String toString() {
            return "LinkNode{" +
                    "num=" + num +
                    ", name='" + name + '\'' +
                    ", nickName='" + nickName + '\'' +
                    '}';
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值