《Java数据结构与算法》链表

1 单链表

链表是有序的列表,但是它在内存中并不一定是连续存储的
在这里插入图片描述
链表是以节点的方式来存储的,每个节点包括data域和next域,链表分为带头节点链表 和 不带头节点链表(根据实际需求确定)

在这里插入图片描述

1.1 单链表的创建和遍历(无序)

将每一个节点增加到单链表的尾部,不考虑节点内部的顺序
在这里插入图片描述

头节点作为基准,不能移动
定义一个temp作为临时节点来控制增加和遍历

在这里插入图片描述

/**
 * @author 雫
 * @date 2021/3/15 - 11:50
 * @function 单链表
 * 将新加入的节点存到当前链表尾部
 */
@SuppressWarnings("all")
public class MySinglyLinkedList {
    //先初始化一个头节点,头节点不能改动
    private Node head;

    //辅助节点,用来控制链表
    private Node temp;

    public MySinglyLinkedList() {
        this.head = new Node(0, "", "");
        this.temp = head;
    }

    /*添加节点,每次将节点加到最后*/
    public void add(Node node) {
        temp.next = node;
        temp = node;
    }

    /*遍历链表,先判断链表是否为空
    * 除了头节点,剩下的节点全部打印一次*/
    public void list() {
        if(head.next == null) {
            throw new RuntimeException("当前链表为空");
        }

        temp = head;
        while(true) {
            if(temp.next == null) {
                break;
            }
            temp = temp.next;
            System.out.println(temp);
        }
    }

}

但是这样加入节点,每次加入的节点都会被放到链表的尾部,如果加入节点的编号是1,3,2,7,6,那么在链表中的顺序也是1,3,2,7,6,现在想要根据节点的顺序,将节点有序地插入链表,即插入顺序是1,3,2,7,6,需要链表中的顺序是1,2,3,6,7

1.2 单链表的创建和遍历(有序)

添加节点时,根据节点内部的编号有序地加入链表
在这里插入图片描述

先通过遍历找到新节点的位置
再将新节点插入到链表中
/**
 * @author 雫
 * @date 2021/3/15 - 13:51
 * @function 单链表
 * 根据新节点的编号将其有序地插入到链表
 */
@SuppressWarnings("all")
public class MySinglyLinkedList2 {
    private Node head;
    private Node temp;

    public MySinglyLinkedList2() {
        this.head = new Node(0, "", "");
        this.temp = head;
    }

    /*根据节点的no来有序地插入链表
    * 链表中某个节点A的no比no大,则将新节点加入到A之前
    * 定义一个flag用来表示链表中的节点的no是否和新节点的no重复*/
    public void add(Node node) {
       boolean flag = false;

        while (true) {
            if (temp.next == null) { //已经遍历到了链尾
                break;
            } else if(temp.next.no > node.no) { //找到了应该插入的位置
                break;
            } else if(temp.next.no == node.no) { //发现重复,更改flag报错
                flag = true;
                break;
            }
            temp = temp.next; //让temp后移
        }

        if(flag) {
            throw new RuntimeException("要插入的节点的no在链表中已重复");
        }
        node.next = temp.next;
        temp.next = node;
    }

    /*遍历链表*/
    public void list() {
        if(head.next == null) {
            throw new RuntimeException("当前链表为空");
        }


        temp = head;
        while(true) {
            if(temp.next == null) {
                break;
            }
            temp = temp.next;
            System.out.println(temp);
        }
    }

}

1.3 根据编号修改节点

	/*修改节点,根据node的no来修改
    * 遍历链表,找到相等的no,替换掉该节点除了no和next的其它值*/
    public void update(Node node) {
        if(head.next == null) {
            throw new RuntimeException("当前链表为空");
        }

        while (true) {
            if(temp.next == null) {
                throw new RuntimeException("要修改的节点不存在");
            }

            if(temp.no == node.no) {
                temp.name = node.name;
                temp.nick = node.nick;
                break;
            }
            temp = temp.next;
        }
    }

1.4 根据编号删除节点

在这里插入图片描述
被删除的节点被链表排除出去,会被GC回收

	/*删除节点,先遍历根据no找到待删除的节点的前一个节点A
    * 让A.next = A.next.next即可*/
    public void remove(Node node) {
        if(head.next == null) {
            throw new RuntimeException("当前节点为空");
        }

        temp = head;
        while (true) {
            if(temp.next == null) {
                throw new RuntimeException("被删除的节点不存在");
            }

            if(temp.next.no == node.no) {
                temp.next = temp.next.next;
                break;
            }
            temp = temp.next;
        }
    }

1.5 单链表习题

1.5.1 求单链表中节点的个数

遍历单链表即可,每遍历一个有效节点,value++

	/*获取单链表中有效元素个数*/
    public int getCount() {
        if (head.next == null) {
            return 0;
        }

        int value = 0;
        temp = head;
        while (true) {
            if(temp.next == null) {
                break;
            }
            temp = temp.next;
            value++;
        }

        return value;
    }

1.5.2 查找单链表中的倒数第K个节点

将倒数的第k个节点转换成正向的第m个节点,遍历整个链表,每经过一个有效节点,value++,直到value等于m,即是需要找到的倒数第K个节点

m = (有效节点总数 - k + 1)
/*获取倒数第K个节点*/
    public Node getNodeByReverseK(int k) {
        if(head.next == null) {
            System.out.println("当前链表为空");
            return null;
        }

        int value = 0;
        int count = (getLength() - k + 1);
        Node result = null;
        temp = head;
        while (true) {
            temp = temp.next;
            value++;

            if(value == count) {
                result = temp;
                break;
            }

            if(temp.next == null) {
                break;
            }
        }
        return temp;
    }

1.5.3 单链表的反转

在这里插入图片描述
在这里插入图片描述

	/*反转单链表*/
    public void reverse() {
        if(this.head.next == null || this.head.next.next == null) {
            System.out.println("当前链表无需反转");
            return;
        }

        temp = head.next; //temp用来遍历原单链表
        Node next = null; //记录原单链表当前temp节点的下一个节点
        Node reverseHead = new Node(0, "", ""); //作为反转后单链表的头节点

        while (temp != null) {
            next = temp.next; //保存当前节点的下一个节点
            temp.next = reverseHead.next; //将节点逆序连接
            reverseHead.next = temp; //将temp所指的节点连接到新链表上
            temp = next; //temp后移
        }
        head.next = reverseHead.next;
    }

1.5.4 反向遍历单链表

有两种方法:
方式一:先将单链表反转,再打印
但这样会破坏原先单链表的结构,不可取

方式二:利用栈,将各个节点顺序入栈
用栈的先进后出,就完成了逆序打印

    /*反向打印单链表,使用栈的先进后出完成*/
    public void reversePrint() {
        if(head.next == null) {
            System.out.println("当前栈为空");
            return;
        }

        Stack<Node> stack = new Stack();
        temp = head;
        while (true) {
            if(temp.next == null) {
                break;
            }

            temp = temp.next;
            stack.push(temp);
        }

        int size = stack.size();
        for(int i = 0; i < size; i++) {
            System.out.println(stack.pop());
        }
        
    }

2 双向链表

单链表有两个明显缺点:
1,单链表查找方向只能是一个方向
2,单链表不能自我删除,需要辅助节点(temp)

为此可以在节点中再增加一个pre指向该节点的前一个节点,来构成更方便的双向链表
在这里插入图片描述

2.1 双向链表的创建和遍历(无序)

双向链表的节点:

/**
 * @author 雫
 * @date 2021/3/15 - 11:50
 * @function 双向链表中的节点
 */
@SuppressWarnings("all")
public class Node {
    public int no;
    public String name;
    public String nick;
    public Node next;
    public Node pre;

    public Node(int no, String name, String nick) {
        this.no = no;
        this.name = name;
        this.nick = nick;
    }

    @Override
    public String toString() {
        return "Node{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nick='" + nick + '\'' +
                '}';
    }

}

双向链表:

/**
 * @author 雫
 * @date 2021/3/16 - 8:34
 * @function 双向链表
 */
@SuppressWarnings("all")
public class DoubleLinkedList {
    private Node head;
    private Node temp;

    public DoubleLinkedList() {
        this.head = new Node(0, "", "");
        this.temp = head;
    }

    public Node getHead() {
        return head;
    }

    public void add(Node node) {
        temp = head;
        while (true) {
            if(temp.next == null) {
                break;
            }
            temp = temp.next;
        }

        temp.next = node;
        node.pre = temp;
    }

    public void list() {
        temp = head;
        while (true) {
            if(temp.next == null) {
                break;
            }
            temp = temp.next;
            System.out.println(temp);
        }
    }

    public void update(Node node) {
        if(head.next == null) {
            System.out.println("当前链表为空!");
            return;
        }

        temp = head;
        while (true) {
            if(temp.next == null) {
                throw new RuntimeException("需要修改的节点不存在");
            }

            if(temp.no == node.no) {
                temp.name = node.name;
                temp.nick = node.nick;
                break;
            }
            temp = temp.next;
        }
    }

    public void remove(Node node) {
        if(head.next == null) {
            System.out.println("当前链表为空");
            return;
        }

        temp = head.next;
        while (true) {
            if(temp == null) {
                throw new RuntimeException("要删除的节点不存在");
            }

            if(temp.no == node.no) {
                temp.pre.next = temp.next;
                if(temp.next != null) { //删除最后一个节点时应该控制,避免空指针异常
                    temp.next.pre = temp.pre;
                    break;
                }
                break;
            }
            temp = temp.next;
        }
    }

}

2.2 双向链表的创建和遍历(有序)

只需要更改add方法即可,找到比要插入的节点的no值大的目标节点,并将插入节点插入到目标节点之前

    public void addBySort(No de node) {
        temp = head;

        while (true) {
            if(temp.no == node.no) {
                throw new RuntimeException("要增加的节点的编号在链表中已重复");
            }

            if(temp.no > node.no) {
                temp.pre.next = node;
                node.pre = temp.pre;
                node.next = temp;
                temp.pre = node;
                break;
            }

            if(temp.next == null) {
                temp.next = node;
                node.pre = temp;
                break;
            }
            temp = temp.next;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值