首页 > 编程语言 >数据结构与算法(三):单向链表

数据结构与算法(三):单向链表

时间:2023-07-31 21:33:07浏览次数:33  
标签:Node head temp 单向 链表 getNextNode 数据结构 节点

链表定义

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑是通过链表种的指针链接次序实现的。链表由一系列节点组成,每个节点包括两部分:一个是存储数据元素的数据域,一个是存储下一个节点地址的指针域。单向链表从头节点(也可以没有头节点)开始,指针指向下一个节点的位置,只能由上一个节点指向后面节点,后面节点不能指向前面节点。单向链表示意图

节点结构

节点包含了数据域和指针域。数据域存放该节点存放的数据信息,指针域存放下一个节点的存储地址。no、nickName、name为该节点的数据内容,nextNode为下一个节点。

public class Node {
    private Integer no;
    private String name;
    private String nickName;
    private Node nextNode;
    public Node(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }
    @Override
    public String toString() {
        return "Node{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

链表操作

初始化单向链表

初始化链表的头节点,头节点数据域为空,指针域为下一个节点的位置。

private Node head = new Node(0, "", "");

遍历链表

因为单向链表的头节点不能移动,所以借助一个临时节点变量进行遍历。

public void showLinkList() {
    if (head.getNextNode() == null) {
        System.out.println("链表为空");
        return;
    }
    Node temp = head.getNextNode();
    while (true) {
        if (temp == null) {
            break;
        }
        System.out.println(temp);
        temp = temp.getNextNode();
    }
}

插入元素

该方法只是将新的节点放到节点的末尾去,并不考虑顺序等因素。

public void addNode(Node node) {
   // 因为head是不能动的,所以需要借助一个临时变量
   Node temp = head;
   while (true) {
       if (temp.getNextNode() == null) {
           break;
       }
       temp = temp.getNextNode();
   }
   temp.setNextNode(node);
}

该方法将新的节点放到节点中去,考虑节点的顺序(以节点的 no 属性为顺序)。

public void addSortNode(Node node) {
    Node temp = head;
    boolean isExist = false;
    while (true) {
        if (temp.getNextNode() == null) {
            break;
        }
        // 对应的序号已经存在
        if (temp.getNo().equals(node.getNo())) {
            isExist = true;
            break;
        }
        if (temp.getNextNode().getNo() > node.getNo()) {
            break;
        }
        temp = temp.getNextNode();
    }
    if (isExist) {
        System.out.println("插入的序号 " + node.getNo() + " 已存在");
    } else {
        node.setNextNode(temp.getNextNode());
        temp.setNextNode(node);
    }
}

更新节点

更新某一个节点的数据域内容

public void updateNode(Node node) {
    if (head.getName().equals("")) {
        System.out.println("链表为空");
    }
    Node temp = head.getNextNode();
    while (true) {
        if (temp == null) {
            break;
        }
        if (temp.getNo() == node.getNo()) {
            break;
        }
        temp = temp.getNextNode();
    }
    if (temp == null || temp.getNo() != node.getNo()) {
        System.out.println("没有找到对应的节点");
    } else {
        temp.setName(node.getName());
        temp.setNickName(node.getNickName());
    }
}

删除节点

找到要删除节点的上一个节点,将该节点的指针域设置成要删除节点的下一个节点,这样要删除的节点就没有指针指向了,就会被GC回收,达到删除节点的目的。

public void deleteNode(Integer no) {
    if (head.getNextNode() == null) {
        System.out.println("链表为空");
    }
    Node temp = head;
    while (true) {
        if (temp.getNextNode() == null) {
            break;
        }
        if (temp.getNextNode().getNo() == no) {
            break;
        }
        temp = temp.getNextNode();
    }
    if (temp.getNextNode() == null) {
        System.out.println("未找到节点");
        return;
    }
    temp.setNextNode(temp.getNextNode().getNextNode());
}

有效节点的个数

public Integer getListNodeConunt() {
    if (head.getNextNode() == null) {
        return 0;
    }
    Node temp = head.getNextNode();
    Integer count = 0;
    while (true) {
        if (temp == null) {
            break;
        }
        count++;
        temp = temp.getNextNode();
    }
    return count;
}

倒数{}个节点

因为单向链表的后一个节点是无法获取到前一个节点的位置的,所以我们先计算出该链表的有效节点个数,再用有效节点个数 - 倒数的节点个数就是正数的节点个数,这样可以达到同样的效果。

public Node getLastNode(SingleLinkList list, Integer index) {
    Integer count = list.getListNodeConunt();
    if (Optional.of(list).isEmpty()) {
        return null;
    }
    // 第几个元素
    Integer indexPosition = 0;
    Node temp = head.getNextNode();
    while (true) {
        if (temp == null) {
            break;
        }
        // 因为链表是单向的,只能从前往后找,倒数第几个=总数-倒数的数
        if (indexPosition == (count - index)) {
            return temp;
        }
        indexPosition++;
        temp = temp.getNextNode();
    }
    return null;
}

链表反转

遍历旧链表,第一个节点放入新链表的第一个节点,第二个节点的下一个节点设置成新链表的第一个节点,以此类推,旧链表的第一个就成了新链表的最后一个节点,第二个节点就成了新链表的倒数第二个节点...

public void getReverseList(Node head) {
    // 如果当前链表为空,或者只有一个节点,无需反转,直接返回
    if (head.getNextNode() == null || head.getNextNode().getNextNode() == null) {
        return;
    }
    // 定义一个辅助变量,帮助遍历原来的链表
    Node cur = head.getNextNode();
    // 指向当前节点cur的下一个节点
    Node next = null;
    Node reverseHead = new Node(0, "", "");
    // 遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
    while (cur != null) {
        // 先暂时保存当前节点的下一个节点,因为后面需要使用
        next = cur.getNextNode();
        // 将cur的下一个节点指向新的链表的最前端
        cur.setNextNode(reverseHead.getNextNode());
        reverseHead.setNextNode(cur);
        // 让cur后移
        cur = next;
    }
    // 将head.next 指向reverseHead.next,实现单链表的反转
    head.setNextNode(reverseHead.getNextNode());
}

逆序打印

利用栈结构先进后出的特点,将遍历的链表节点依次入栈,再打印栈中的节点,这样可以在不改变原来链表数据的情况下进行逆序打印输出。

public void reversePrint(Node head) {
    if (head.getNextNode() == null) {
        return;
    }
    // 创建一个栈,将各个节点压入栈
    Stack<Node> stack = new Stack<>();
    Node cur = head.getNextNode();
    while (cur != null) {
        stack.push(cur);
        cur = cur.getNextNode();
    }
    while (stack.size() > 0) {
        System.out.println(stack.pop());
    }
}

标签:Node,head,temp,单向,链表,getNextNode,数据结构,节点
From: https://www.cnblogs.com/wangms821/p/17594543.html

相关文章

  • 线性数据结构和 STL
    vector容器(container)定义及头文件引入定义:一个可变长数组头文件:#include<vector>常用变量定义及函数解析end():尾后迭代器。push_back(x):在末端插入元素x(自动扩容)。构造函数一个参数:建立长度为n的数组:vector<int>a(n);两个参数:建立长度为n,每个元素的值均为......
  • 反转单向链表 | 空间复杂度O(1)
    反转单向链表时间复杂度:O(N)空间复杂度:O(1)voidreverse_list(node**head_ptr){ node*prev=NULL; node*curr=*head_ptr; node*next=NULL; while(curr!=NULL){ /*INSERTCODEHERE*/ next=curr->next; curr->next=prev; prev=curr; ......
  • 143. 重排链表
    143.重排链表  给定一个单链表L的头节点head,单链表L表示为:请将其重新排列后变为:不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 示例1:输入:head=[1,2,3,4]输出:[1,4,2,3]示例2:输入:head=[1,2,3,4,5]输出:[1,5,2,4,3] 提示:链......
  • 基础树形数据结构
    基础树形数据结构0.前言某个MXY问我为什么要讲树形数据结构。原因就是因为它复杂码量大可以装逼,还可以出一点毒瘤题,最重要的是我第一个学的难的知识就是这个能对于修改和查询的优化。下面是四个典型数据结构时间复杂度的比较↓数组前缀和线段树傻子社长树状数组......
  • 代码随想录第四天|力扣24.两两交换链表节点、力扣19.删除链表的倒数第N个结点、力扣面
    两两交换链表中的节点(力扣24.)dummyhead.next=head;cur=dummyhead;while(cur.next!=null&&cur.next.next!=null)temp=cur.next;temp1=cur.next.next.next;cur.next=cur.next.next;cur.next.next=temp;temp.next=temp1;cur=cur.next.next;returndummyhead.n......
  • Map和Object:JS如何根据需求选择正确的键值对数据结构
    Map和Object都是JavaScript中常用的数据结构,它们都可以用来存储键值对(key-valuepairs)。但是,它们之间也有一些重要的区别,了解这些区别可以帮助我们选择更合适的数据结构来满足我们的需求。公众号:Code程序人生,个人网站:https://creatorblog.cnObject的特点Object是JavaScript中最基本......
  • 【ACM专项练习#02】整行字符串、输入vector、打印图形、处理n组数据以及链表操作等
    输入整行字符串平均绩点题目描述每门课的成绩分为A、B、C、D、F五个等级,为了计算平均绩点,规定A、B、C、D、F分别代表4分、3分、2分、1分、0分。输入有多组测试样例。每组输入数据占一行,由一个或多个大写字母组成,字母之间由空格分隔。输出每组输出结果占一行。如果输入的大......
  • 【个人模板封装】树套树、高维数据结构
    前言这是我个人使用的一些模板封装,限于个人能力,可能存在诸多不足与漏洞,在未加测试直接使用前请务必小心谨慎。更新可能会滞后于我本地的文档,如有疑问或者催更之类的可以在评论区留言。全文模板测试均基于以下版本信息,请留意版本兼容问题。Windows,64bitG++(ISOC++20)stack......
  • 代码随想录算法训练营第四天| LeetCode 24. 两两交换链表中的节点 19.删除链表的倒
    24.两两交换链表中的节点     卡哥建议:用虚拟头结点,这样会方便很多。 本题链表操作就比较复杂了,建议大家先看视频,视频里我讲解了注意事项,为什么需要temp保存临时节点。   题目链接/文章讲解/视频讲解:https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%......
  • 考研数据结构——每日一题[表达式求值]
    3302.表达式求值给定一个表达式,其中运算符仅包含+,-,*,/(加减乘整除),可能包含括号,请你求出表达式的最终值。注意:数据保证给定的表达式合法。题目保证符号-只作为减号出现,不会作为负号出现,例如,-1+2,(2+2)*(-(1+1)+2)之类表达式均不会出现。题目保证表达式中所有数字均为......