【数算-03】单向链表

1、定义单向链表中每个结点的数据结构

package common;

/**
 * @author zhihua.li
 * @date 2021/1/20 - 10:16
 **/

public class Node {
    private Integer no;
    private String name;
    private String nickName;
    
    public Integer getNo() {
        return no;
    }

    public void setNo(Integer no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    private Node next;


    public Node(Integer no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    public Node next(){
        return next;
    }


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

2、单向链表的初始化与操作

1、初始化实质上就是设置一个头结点
public Node headNode = new Node(0, "", "");
2、添加结点到单向链表中
/*
        添加节点到链表中,直接添加到链表尾部
        找到当前链表的最后一个节点,并将其指针域指向要添加的新节点
    */
    public void addNode(Node node) {
        Node temp = headNode;
//        找到当前链表的最后一个元素
        while (true) {
            if (temp.next() == null) {
                break;
            }
            temp = temp.next();
        }

//        设置其指针域为目标节点
        temp.setNext(node);



    }
3、 每次将结点添加到到链表的头结点之后,保证新添加的节点总是链表头结点的下一个结点
//    将结点每次都添加到链表头结点的下一个节点
    public void addToHead(Node node) {
        Node temp = headNode;
        if (temp.next() == null) {
            temp.setNext(node);
            return;
        }
        node.setNext(temp.next());
        temp.setNext(node);
    }
4、按序将结点添加到链表
//    插入链表的指定位置(按no顺序)
    public void addToDes(Node node) {
//        设置工具节点,用来遍历链表
        Node temp = headNode;
//        设置一个标志位,若为 true,说明插入失败
        boolean flag = false;
//        找到当前链表的最后一个元素
        while (true) {
//            找到链表尾,break
            if (temp.next() == null) {
                break;
            }
//            如果当前遍历到的节点的下一位序号大于要插入的节点序号,说明插入位置就是当前遍历到的节点的下一位
            if (temp.next().getNo() > node.getNo()) {
                break;
//            要插入的节点的序号已经存在在链表中,就不能再往里面插入了
            } else if (temp.next().getNo() == node.getNo()) {
                flag = true;
                break;
            }
            temp = temp.next();
        }

//        无法插入的情况
        if (flag) {
            System.out.println("准备插入的节点已经在链表中存在,插入失败!");
        }
//        成功插入的情况
        else {
            node.setNext(temp.next());
            temp.setNext(node);
        }


    }
5、打印整个链表
//    通过遍历显示整个链表数据
    public void listLinkedList() {

        if (headNode.next() == null) {
            System.out.println("链表为空");
            return;
        }

//        生成临时节点进行链表遍历
        Node temp = headNode.next();
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
//            将temp后移
            temp = temp.next();

        }

    }
6、根据结点序号对结点信息进行修改
//    根据节点的序号进行修改信息(注意:不能修改结点的no和next属性,均可能对其他元素产生影响)
    public void updateNode(Node node) {
        if (headNode.next() == null) {
            System.out.println("链表为空");
            return;
        }
        Node temp = headNode.next();
        boolean flag = false;

        while (true) {
//            链表已经遍历完
            if (temp == null) {
                break;
            }
//            如果当前temp的no已经大于node的no了,在有序的情况下,以后也不可能有匹配的了,所以直接返回
            if (temp.getNo() > node.getNo()) {
                return;
            }
//            找到了与修改节点no相同的结点
            else if (temp.getNo() == node.getNo()) {
                flag = true;
                break;
            }

            temp = temp.next();
        }
        if (flag) {
            temp.setName(node.getName());
            temp.setNickName(node.getNickName());
        } else {
//            没找到的情况
            System.out.println("修改失败!");
        }
    }
7、删除结点
public void deleteNode(Integer no) {
        Node temp = headNode;
        boolean flag = false;
        while (true) {
            if (temp.next() == null) {
                break;
            }
            if (temp.next().getNo() == no) {
                flag = true;
                break;
            }
            temp = temp.next();
        }
        if (flag) {
            temp.setNext(temp.next().next());
        } else {
            System.out.println("删除成功!");
        }
    }
8、获取单链表长度
//    获取单链表长度(不统计头结点)
    public  int listLength() {
        int length = 0;
        Node node = headNode;
        while (node.next() != null) {
            length++;
            node = node.next();
        }
        return length;
    }
9、查找单链表中正数第n个结点
//    查找单链表中第k个节点
    //    (使用的时候index从1开始!)
    public Node indexNode( Integer index) {
        Node node = headNode.next();
        int length = listLength();
        if (index <= 0 || index > length) {
            return null;
        }
        for (int i = 0; i < index-1; i++) {
            node = node.next();
        }
        return node;
    }
   
10、 查找单链表中倒数第n个结点
//    查找单链表中倒数第k个节点
    public Node lastIndexNode(Integer index) {
        Node node = headNode.next();
        int length = listLength();
        if (index <= 0 || index > length) {
            return null;
        }
//       倒数第一个从数组的角度考虑其实是第零个
        for (int i = 0; i < length - index; i++) {
            node = node.next();
        }
        return node;
    }
小结:整个单链表操作类
package single_linked_list;

import com.sun.deploy.panel.ITreeNode;
import com.sun.xml.internal.ws.api.pipe.NextAction;
import common.Node;

/**
 * @author zhihua.li
 * @date 2021/1/20 - 10:16
 * <p>
 * 管理节点的类
 **/

public class SingleLinkedList {
    //    初始化头结点,头结点不存放具体的数据
    public Node headNode = new Node(0, "", "");

    /*
        添加节点到链表中,直接添加到链表尾部
        找到当前链表的最后一个节点,并将其指针域指向要添加的新节点
    */
    public void addNode(Node node) {
        Node temp = headNode;
//        找到当前链表的最后一个元素
        while (true) {
            if (temp.next() == null) {
                break;
            }
            temp = temp.next();
        }

//        设置其指针域为目标节点
        temp.setNext(node);



    }

    //    将结点每次都添加到链表头结点的下一个节点
    public void addToHead(Node node) {
        Node temp = headNode;
        if (temp.next() == null) {
            temp.setNext(node);
            return;
        }
        node.setNext(temp.next());
        temp.setNext(node);
    }

    //    插入链表的指定位置(按no顺序)
    public void addToDes(Node node) {
//        设置工具节点,用来遍历链表
        Node temp = headNode;
//        设置一个标志位,若为 true,说明插入失败
        boolean flag = false;
//        找到当前链表的最后一个元素
        while (true) {
//            找到链表尾,break
            if (temp.next() == null) {
                break;
            }
//            如果当前遍历到的节点的下一位序号大于要插入的节点序号,说明插入位置就是当前遍历到的节点的下一位
            if (temp.next().getNo() > node.getNo()) {
                break;
//            要插入的节点的序号已经存在在链表中,就不能再往里面插入了
            } else if (temp.next().getNo() == node.getNo()) {
                flag = true;
                break;
            }
            temp = temp.next();
        }

//        无法插入的情况
        if (flag) {
            System.out.println("准备插入的节点已经在链表中存在,插入失败!");
        }
//        成功插入的情况
        else {
            node.setNext(temp.next());
            temp.setNext(node);
        }


    }

    //    通过遍历显示整个链表数据
    public void listLinkedList() {

        if (headNode.next() == null) {
            System.out.println("链表为空");
            return;
        }

//        生成临时节点进行链表遍历
        Node temp = headNode.next();
        while (true) {
            if (temp == null) {
                break;
            }
            System.out.println(temp);
//            将temp后移
            temp = temp.next();

        }

    }

    //    根据节点的序号进行修改信息(注意:不能修改结点的no和next属性,均可能对其他元素产生影响)
    public void updateNode(Node node) {
        if (headNode.next() == null) {
            System.out.println("链表为空");
            return;
        }
        Node temp = headNode.next();
        boolean flag = false;

        while (true) {
//            链表已经遍历完
            if (temp == null) {
                break;
            }
//            如果当前temp的no已经大于node的no了,在有序的情况下,以后也不可能有匹配的了,所以直接返回
            if (temp.getNo() > node.getNo()) {
                return;
            }
//            找到了与修改节点no相同的结点
            else if (temp.getNo() == node.getNo()) {
                flag = true;
                break;
            }

            temp = temp.next();
        }
        if (flag) {
            temp.setName(node.getName());
            temp.setNickName(node.getNickName());
        } else {
//            没找到的情况
            System.out.println("修改失败!");
        }
    }

    //    删除节点
    public void deleteNode(Integer no) {
        Node temp = headNode;
        boolean flag = false;
        while (true) {
            if (temp.next() == null) {
                break;
            }
            if (temp.next().getNo() == no) {
                flag = true;
                break;
            }
            temp = temp.next();
        }
        if (flag) {
            temp.setNext(temp.next().next());
        } else {
            System.out.println("删除成功!");
        }
    }

    //    获取单链表长度(不统计头结点)
    public  int listLength() {
        int length = 0;
        Node node = headNode;
        while (node.next() != null) {
            length++;
            node = node.next();
        }
        return length;
    }

    //    查找单链表中第k个节点
    //    (使用的时候index从1开始!)
    public Node indexNode( Integer index) {
        Node node = headNode.next();
        int length = listLength();
        if (index <= 0 || index > length) {
            return null;
        }
        for (int i = 0; i < index-1; i++) {
            node = node.next();
        }
        return node;
    }

    //    查找单链表中倒数第k个节点
    public Node lastIndexNode(Integer index) {
        Node node = headNode.next();
        int length = listLength();
        if (index <= 0 || index > length) {
            return null;
        }
//       倒数第一个从数组的角度考虑其实是第零个
        for (int i = 0; i < length - index; i++) {
            node = node.next();
        }
        return node;
    }

}

3、对单链表的一些高级操作

1、链表反转
    public void reverseList(Node node) {
/*    链表反转:
    解法一(利用之前的api):
    利用已经写出的获取倒数第n个结点的api,反向生成一个新的链表
        Node node = list.headNode.next();
        SingleLinkedList result = new SingleLinkedList();
//        int length = list.listLength(list);
//        for (int i = 1; i <= length; i++) {
//            Node node = list.lastIndexNode(list, i);
//            Node newNode = new Node(node.getNo(), node.getName(), node.getNickName());
//            result.addNode(newNode);
//        }
//        return result;

    解法二(利用之前的api):
    利用已经写出的将要插入的结点插入头结点后的api,遍历原链表并依次将结点添加到新链表的头结点后
        int length = list.listLength(list);
        for (int i = 0; i < length; i++) {
            if (node != null) {
                result.addToHead(new Node(node.getNo(),node.getName(),node.getNickName()));
            }
            node = node.next();
        }

        return result;
    }*/
    
//    解法三(老师解法)
//        当前链表为空或者只有一个节点时,不反转直接返回
        if (node.next() == null || node.next().next() == null) {
            return;
        }

//        定义辅助指针遍历原来的链表
        Node cur = node.next();
//        指向当前节点的下一个节点
        Node next = null;
        Node reverseHead = new Node(0, "", "");
        while (cur != null) {
            next = cur.next();
            cur.setNext(reverseHead.next());    /*将cur的next设置为reverse的next*/
            reverseHead.setNext(cur);   /*将cur插入到reverse的链表头*/
            cur = next;
        }
        node.setNext(reverseHead.next());
    }
2、逆序打印单链表(利用数据结构:栈

注意该题目中只是逆序打印单链表,并没有改变单链表的结构

//    逆序打印单链表
    public void printReverseLinkedList(SingleLinkedList list) {
        Node headNode = list.headNode;
        if (headNode.next() == null) {
            return;     //空链表不能打印
        }
//        初始化栈
        Stack<Node> stack = new Stack<>();
        Node node = headNode.next();
//        链表入栈
        while (node != null) {
            stack.push(node);
            node = node.next();
        }
//        链表元素出栈
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }
3、合并有序列表
//    合并两有序链表
    public static SingleLinkedList combineOrderdLinkedList(SingleLinkedList s1, SingleLinkedList s2) {
//        初始化结果链表
        SingleLinkedList result = new SingleLinkedList();
//        初始化结果链表的头结点及其next
        Node node = result.headNode;
        Node next = node.next();
//        获取两个链表的长度
        int length1 = s1.listLength();
        int length2 = s2.listLength();
//        声明辅助变量帮助循环
        int i = 1;
        int j = 1;

        while (true) {
            if (i > length1) {
//                如果s1中的所有元素均被添加到了result链表中,就将s2中的剩余所有元素顺序的添加到result中
                while (j <= length2) {
                    result.addNode(new Node(s2.indexNode(j)));
                    j++;
                }
                break;
            }
            if (j > length2) {
//                同理,如果s2中的所有元素均被添加到了result中,那么s1中的剩余全部元素均顺序添加到result中
                while (i <= length1) {
                    result.addNode(new Node(s1.indexNode(i)));
                    i++;
                }
                break;
            }
//            依次比较s1和s2链表中的元素的no,使结果链表有序
            if (s1.indexNode(i).getNo() < s2.indexNode(j).getNo()) {
                result.addNode(new Node(s1.indexNode(i)));
                i++;
            } else if (s1.indexNode(i).getNo() > s2.indexNode(j).getNo()) {
                result.addNode(new Node(s2.indexNode(j)));
                j++;
            }

        }
        return result;
    }

注意:在这个api中又修改了Node类,为其添加了新的Node的构造方法

public Node(Node node){
        no = node.no;
        name = node.name;
        nickName = node.nickName;
    }

在做最后这道题的时候,一直不知道怎么处理添加某个结点时,仅仅添加这一个结点,而事实上在结点添加时,总会带着它的next域一起添加进来

通过上面的构造方法,可以做到在添加某个节点时,不添加它的next域,而只是把它的信息赋值到一个新节点上添加到新链表中

至此,单向链表完,用时3天

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

pascalzhli

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

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

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

打赏作者

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

抵扣说明:

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

余额充值