🧡数据结构与算法🧡从零到有系列《三》链表

367 阅读4分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

 🎨链表

✨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;

    }

🎃5. 双向链表应用场景

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

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("要删除的结点不存在");

        }

 

    }

}

🎄6.单向环形链表应用场景

约瑟夫问题(约瑟夫环)

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

💎7. 单向环形链表介绍

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

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** = 
***********/**\
***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* *后移**\
***}\
    }\
     *//* *根据用户的输入,计算出小孩节点出圈的顺序**\
*

*/****\
**** ***@company:* ***北京动力节点**\
****** ***@author:* *****韩国庆******\

***/***\
**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); *//* *从第一个开始,每两个小孩出去一次,一共有五个小孩**\
***}\
\
}