数据结构—链表

一、 链表简介

链表是有序的列表,存储方式是链式存储

特点

1)链表是以节点的方式来存储

2)每个结点包含data域,next域(指向下一个结点)

3)链表的各个结点不一定是连续存储的

4)链表分为【带头结点】的链表和【没有头结点】的链表,根据实际需求来确定。

二、单向链表

1.添加节点

1)不考虑节点的编号顺序时

	1)借助辅助指针temp找到当前链表的最后结点

	2)将最后一个结点的next域指向一个新的结点

2)考虑节点编号顺序时

	因为是单链表,所以我们找的辅助指针temp是位于添加位置的【前一个】结点,否则无法添加,简单理解为:单向链表只能指向后面的结点,不能指向前面的结点

2.修改节点

借助辅助指针temp,找到待修改节点即可。

3 删除节点

1)因为是单向链表,所以辅助变量temp需要找到需要删除的这个结点的【前一个】结点。如果temp找到的就是需要删除的结点,是删不掉的

4.求链表中有效节点个数

借助辅助指针temp,temp指向链表第一个有效节点,即head后面的节点,然后依次遍历链表并计数。

5.查找链表中倒数第k个节点

1)先获取链表的优先节点个数

2)从第一个有效节点开始遍历(length - index)次即可。也就是让辅助指针temp指向第一个有效节点,移动(length - index)次

6.单向链表反转

在原链表中遍历【取出】每一个结点,使用【添加操作】,添加到一个新的链表中,进而实现反转。

7.单向环形链表

1)可以解决Josephus问题

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

2)创建单向环形链表

1)先创建第一个结点,让first指针指向该结点,形成环形

2)后面每创建一个新的结点,就把该节点,加入到已有的环形链表中即可。

3)遍历环形链表

1)先让辅助节点temp,指向first节点

2)然后通过while循环遍历该环形链表即可,当temp.next == first时,循环结束。

3)简单理解,就是first指向第一个,temp指向最后一个,然后同时移动同样的次数,保证首尾相连构成环形。

三、双向链表

1.单向链表的缺点

1)单向链表只有一个方向,即前向添加、查找节点

2)不能自我删除

2.双向链表的增删改查

1)添加节点

辅助指针temp指向头节点

1)默认添加到链表最后:
	1)temp.setNext(newNode)
	2)newNode.setPre(temp)
	
2)按编号顺序添加:
	temp找到待添加位置的前一个节点
	1)newNode.setNext(temp.getNext())
	2)temp.setNext(newNode)
	3)temp.getNext().setPre(newNode)
	4)newNode.setPre(temp)

2)修改节点

和单向链表修改的方式一样

3)删除节点

因为是双向链表,所以可以实现自我删除,temp直接指向待删除的节点即可。
1)temp.getPre().setNext(temp.getNext())
2)temp.getNext().setPre(temp.getPre())

四、单向环形链表Java代码

public class CircleSingleLinkedList {
    
    
    //创建一个first节点,先不用赋值
    private CircleNode first = new CircleNode(-1);

    /**
     * 添加结点
     * @param nums 表示添加多少个节点
     */
    public void addNode(int nums){
    
    
        if (nums < 1){
    
    
            System.out.println("nums的值不合法!");
            return;
        }
        CircleNode temp = null; //辅助节点,用于构建环形链表
        //使用for循环,创建环形链表
        for (int i = 1; i <= nums; i++) {
    
    
            //根据编号创建节点
            CircleNode newNode = new CircleNode(i);
            //如果是第一个节点,情况比较特殊,创建后应该形成环形
            if (i == 1){
    
    
                first = newNode;
                first.setNext(first);   //构成环形
                temp = first;   //让temp指向第一个节点,因为first指针不能动,所以需要辅助节点构建环形链表
            }else {
    
    
                //1.先让temp节点指向添加的新节点
                temp.setNext(newNode);
                //2.再让新添加的节点指向first
                newNode.setNext(first);
                //3.在最后让temp指向当前的新节点,即temp后移一位
//                temp = newNode;   //与下面这条语句是等效的
                temp = temp.getNext();
            }
        }
    }

    /**
     * 遍历单向环形链表
     */
    public void list(){
    
    
        //首先判断链表是否为空,因为是环形链表,所以判空的条件不能写为:first.getNext() == null
        //因为first.getNext()表示的是first节点的值,即初始值-1
        if (first == null){
    
    
            System.out.println("链表为空!");
            return;
        }
        //因为first不能动,所以需要一个辅助节点
        CircleNode temp = first;
        while (true){
    
    
            System.out.printf("节点的编号为%d\n", temp.getNode());
            if (temp.getNext() == first){
    
    
                //说明已经遍历完毕
                break;
            }else {
    
    
                temp = temp.getNext();  //temp后移
            }
        }
    }

    /**
     * 根据用户的输入,计算出节点出圈的顺序
     * @param startNum 表示从第几个节点开始报数
     * @param countNum 表示数几下
     * @param nums 表示链表中总共有几个节点
     */
    public void deleteByOrder(int startNum, int countNum, int nums){
    
    
        if (first == null || startNum < 1 || startNum > nums){
    
    
            System.out.println("参数输入不合法,请重新输入!");
            return;
        }
        CircleNode temp = first;
        //事先应该将temp指向最后一个节点
        while (true){
    
    
            if (temp.getNext() == first){
    
    
                //说明temp到了最后一个节点了
                break;
            }else {
    
    
                temp = temp.getNext();
            }
        }
        //报数前,需要将first指向开始报数的那个节点,需要将first和temp同时移动startNum-1次
        for (int i = 0; i < startNum -1; i++){
    
    
            first = first.getNext();
            temp = temp.getNext();
        }
        //报数时,first和temp指针同时移动countNum-1次,然后出圈
        //这是个循环的操作,直到圈中只有一个节点
        while (true){
    
    
            if (temp == first){
    
    
                //说明圈中只有一个节点
                break;
            }else {
    
    
                //first和temp指针同时移动countNum-1次,然后出圈
                for (int j = 0; j < countNum - 1; j++) {
    
    
                    first = first.getNext();
                    temp = temp.getNext();
                }
                //for循环结束后,first指向的就是要出圈的节点
                System.out.printf("节点%d出圈成功!\n", first.getNode());
                first = first.getNext();
                temp.setNext(first);
            }
        }
        System.out.printf("最后留在圈中的节点编号为%d\n", first.getNode());
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42214237/article/details/123302683