❤️《数据结构与算法》系列(三)❤️链表

目录

🎨链表

1.链表( Linked List) 介绍

2. 单链表应用

3. 常见面试题

4. 双向链表应用场景

5.单向环形链表应用场景

6. 单向环形链表介绍

最后


🎨链表

1.链表( Linked List) 介绍

链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。

image.png

特点:

1. 链表是以结点形式存储的,是链式存储

2. 每个结点包含data区域和next区域

3. 如上图各个结点并不是连续存储的

4. 链表分带头结点链表和没有带头结点链表,根据实际的需求来确定

带头结点链表逻辑结构

image.png

需求:

根据带有头部的单链表,实现商品增删改查,并且也可以针对商品已编号进行排序,完成排行榜

2. 单链表应用


 * author:韩国庆

 * date:2021/1/23 14:50

 * version:1.0

 */

public class GoodsNode {

 

    public int gId;
    public String gName;

    public double gProce;

 

    //指针指向下一个结点

    public GoodsNode next;

 

    public GoodsNode(int gId, String gName, double gProce) {

        this.gId = gId;

        this.gName = gName;

        this.gProce = gProce;

    }

 

 }

 * author:韩国庆

 * date:2021/1/23 15:23

 * version:1.0

 */

public class DLLinkedList {

 

    private GoodsNode node = new GoodsNode(0,"",0.0);

 

    /**

     * 添加结点

     * @param goodsNode

     */

    public void add(GoodsNode goodsNode){
    GoodsNode temp = node;

        while (true){

            if (temp.next == null){

                break;

            }

            temp = temp.next;

        }

 

        temp.next = goodsNode;

    }

 

    /**

     * 插入结点按照id编号插入

     */

    public void addByOrder(GoodsNode goodsNode){

        GoodsNode temp = node;

        boolean flag = false;

        while (true){

            if (temp.next==null){

                break;

            }

            if (temp.next.gId>goodsNode.gId){

                break;

            }else if (temp.next.gId==goodsNode.gId){//说明已经

                flag = true;

                break;

            }

            temp = temp.next;

        }
        if (flag){

            System.out.println("不能添加该商品,已经存在了...");

        }else {

            goodsNode.next = temp.next;

            temp.next = goodsNode;

        }

    }

 

    /**

     * 修改结点

     * @param goodsNode

     */

    public void updateNode(GoodsNode goodsNode){

        if (node.next==null){

            System.out.println("链表为空...");

            return;

        }

        GoodsNode temp = node.next;

        boolean flag = false;

        while (true){

            if (temp == null){

                break;

            }

            if (temp.gId == goodsNode.gId){

                flag = true;

                break;

            }

            temp = temp.next;

        }
        if (flag){

            temp.gName = goodsNode.gName;

            temp.gProce = goodsNode.gProce;

        }else {

            System.out.println("没有找到该结点");

        }

    }

 

    /**

     * 删除结点

     *

     */

    public void delNode(int gId){

        GoodsNode temp = node;

        boolean flg = false;

        while(true){

            if (temp.next==null){

                break;

            }

            if (temp.next.gId==gId){

                flg = true;

                break;

            }

            temp = temp.next;

        }

 

        if (flg){

            temp.next = temp.next.next;

        }else {

            System.out.println("要删除的信息不存在");
            }

    }

 

}

3. 常见面试题

给定一个链表,实现倒转链表

image.png


        if (goodsNode.next==null || goodsNode.next.next == null){

            return;

        }

 

        GoodsNode point = goodsNode.next;

        GoodsNode next = null;

        GoodsNode reverseNode = new GoodsNode(0,"",0.0);

 

        while (point !=null){

            next = point.next;

            point.next = reverseNode.next;

            reverseNode.next = point;

            point = next;
            }

 

        goodsNode.next = reverseNode.next;

    }

4. 双向链表应用场景

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

image.png


 * author:韩国庆

 * date:2021/1/25 16:45

 * version:1.0

 */

public class GoodsNode2 {

    public int gId;

    public String gName;

    public double gProce;
    //指针指向下一个结点

    public GoodsNode2 next;

    //指针指向上一个结点

    public GoodsNode2 pre;

 

    public GoodsNode2(int gId, String gName, double gProce) {

        this.gId = gId;

        this.gName = gName;

        this.gProce = gProce;

    }

}

 * author:韩国庆

 * date:2021/1/25 16:47

 * version:1.0

 */

public class TwoLinkedList {

 

    private GoodsNode2 node = new GoodsNode2(0,"",0.0);

 

    /**

     *

     * @return  返回头结点

     */

    public GoodsNode2 returnHeadNode(){

        return node;

    }
    /**

     * 添加结点到链表最后面

     */

    public void addLast(GoodsNode2 goodsNode2){

        GoodsNode2 temp = node;

 

       while (true){

           if (temp.next == null){

               break;

           }

 

           temp = temp.next;

       }

       temp.next = goodsNode2;

       goodsNode2.pre = temp;

 

    }

 

    /**

     * 修改结点

     */

    public void updateNode(GoodsNode2 newNode){

        if (node.next==null){

            System.out.println("链表为空");

            return;

        }

        GoodsNode2 temp = node.next;

        boolean flg = false;

        while (flg){

            if (temp == null){
            break;

            }

            if (temp.gId == newNode.gId){

                flg = true;

                break;

            }

            temp = temp.next;

        }

        if (flg){

            temp.gName = newNode.gName;

            temp.gProce = newNode.gProce;

        }else {

            System.out.println("没有找到...");

        }

 

    }

 

    /**

     * 双向链表删除

     */

    public void delNode(int gId){

        if (node.next == null){

            System.out.println("链表为空,无法删除");

            return;

        }

        GoodsNode2 temp = node.next;

        boolean flag = false;

        while (true){

            if (temp == null){

                break;
                }

            if (temp.gId == gId){

                flag = true;

                break;

            }

            temp = temp.next;

        }

        if (flag){

            temp.pre.next = temp.next;

            if (temp.next !=null){

                temp.next.pre = temp.pre;

            }

        }else {

            System.out.println("要删除的结点不存在");

        }

 

    }

}

5.单向环形链表应用场景

约瑟夫问题(约瑟夫环)

设编号为1,2…,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

6. 单向环形链表介绍

采用一个单项环形链表来处理(可以带表头也可以不带表头),具体使用头结点还是不带头结点,后面会具体分析。

image.png

约瑟问题示意图

· n=5,即有5个人

· k=1,从第一个人开始报数

· m=2,数2下

image.png

image.png

image.png

image.png

image.png

/**
 * @company: 北京动力节点
 * @author:韩国庆
 */
public class Boy {

    private int no;//编号
    private Boy next;//指向下一个节点,默认依旧为空

    public Boy(int no){
        this.no = no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public int getNo() {
        return no;
    }
    public Boy getNext() {
        return next;
    }
    public void setNext(Boy next) {
        this.next = next;
    }

}
/**
 * @company: 北京动力节点
 * @author:韩国庆
 */
public class CircleSingleLinkedList {

    //创建一个first节点,当前没有编号
    private Boy first = new Boy(-1);

    //加入小孩节点,构建成环形链表
    public void addBoy(int nums){
        //判断数据真实性,nums做数据校验
        if (nums < 1 ){
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null;//辅助指针,帮助构建环形链表
        //使用for循环来创建环形链表
        for (int i = 1;i <= nums;i ++){
            //根据编号,创建小孩节点
            Boy boy = new Boy(i);
            //如果是第一个小孩
            if (i == 1){
                first = boy;
                first.setNext(first);//构成环状
                curBoy = first;//让curBoy指向第一个小孩
            }else {
                curBoy.setNext(boy);//前面的指向新来的小孩
                boy.setNext(first);//新添加的小孩指向开头,构成环形
                curBoy = boy;//后移
            }
        }
    }
    //遍历当前的环形链表
    public void showBoy(){
        //判断链表是否为空
        if (first == null){
            System.out.println("链表为空~~");
            return;
        }
        //因为first不能动,因此需要辅助指针,完成遍历
        Boy curBoy = first;
        while (true){
            System.out.printf("小孩的编号%d\n",curBoy.getNo());
            if (curBoy.getNext() == first){//说明遍历完毕
                break;
            }
            curBoy = curBoy.getNext();//让curBoy后移
        }
    }
    //根据用户的输入,计算出小孩节点出圈的顺序
   /*
    startNo 表示从第几个小孩开始数数
    countNum表示数几下
    nums 表示 最初有多少小孩在圈
     */
    public void countBoy(int startNo,int countNum,int nums){
        //先对数据进行校验
        if (first == null || startNo < 1 || startNo > nums){
            System.out.println("参数输入有误,请重新输入!");
            return;
        }
        //创建一个辅助指针。帮助我们完成小孩出圈
        Boy helper = first;
        //需要创建一个辅助指针helper,事先应该指向环形链表的最后的这个节点。
        while (true){
            if (helper.getNext() == first){//说明小孩指向最后节点
                break;
            }
            helper = helper.getNext();
        }
        // 报数之前,先让first和helper移动k-1次
        for (int j = 0;j < startNo - 1; j ++){
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数时,让first和helper指针同时的移动m-1次
       //这里是一个循环的操作,直到圈中只有一个节点
        while (true){
            if (helper == first){ //说明圈中只有一个节点
                break;
            }
            //否则让first与helper移动countNum-1次
            for (int j = 0;j < countNum - 1;j ++){
                first = first.getNext();
                helper = helper.getNext();
            }
            //这时first指向的节点,就是要出圈小孩的节点
            System.out.printf("小孩%d出圈\n",first.getNo());
            //这时将first指向的小孩节点出圈
            first = first.getNext();
            helper.setNext(first);//
        }
        System.out.printf("最后留在圈中的小孩编号是%d\n",first.getNo());
    }
}
public class TestApp {

    public static void main(String[] args) {
        //测试,构建环形与遍历是否可行
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);//添加五个小孩节点
        circleSingleLinkedList.showBoy();
        //测试小孩出圈是否正确
        circleSingleLinkedList.countBoy(1,2,5);//从第一个开始,每两个小孩出去一次,一共有五个小孩
    }

}

最后

目前市面上更多的是C语言,C++版的数据结构和算法,极少有关于Java数据结构和算法的课程,所以Java程序员往往需要跨语言学习,难度和效率大大折扣!

今天,也分享给大家一套Java的数据结构和算法教程,一套属于咱Java程序员的数据结构和算法课程,帮助广大Java程序员,系统化深度学习数据结构和算法,掌握其中要领实现华丽转身,进大厂,升职加薪指日可待!

https://www.bilibili.com/video/BV1HQ4y1d7th

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

牛仔码农@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值