单向链表的介绍和实现思路
链表的介绍
链表在内存中的存储
特点
- 链表是以节点的方式来存储,是链式存储
- 每个节点包含 data 域 和 next 域。next域用来指向下一个节点
- 链表的各个节点不一定是连续存储的
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
带头结点的逻辑示意图
实现思路
创建(添加)
- 先创建一个Head头节点,表示单链表的头
- 后面我们每添加一个节点,就放在链表的最后
遍历
- 通过一个辅助变量,来遍历整个链表
有序插入
- 先遍历链表,找到应该插入的位置
- 要插入的节点的next指向插入位置的后一个节点
- 插入位置的前一个节点的next指向要插入节点
- 插入前要判断是否在队尾插入
根据某个属性节点修改值
- 先遍历节点,找到修改的位置
- 如果未找到修改节点,则不修改
删除某个节点
- 先遍历节点,找到要删除节点的前一个节点
- 进行删除操作
求倒数第n个节点的信息
- 遍历链表,求出链表的有效长度length(不算头结点)
- 遍历链表到第length-n的节点
翻转链表
- 创建一个新的头结点,作为新链表的头
- 从头遍历旧链表,将遍历到的节点插入新链表的头结点之后
- 注意需要用到两个暂存节点
- 一个用来保存正在遍历的节点
- 一个用来保存正在遍历节点的下一个节点
逆序打印
- 遍历链表,将遍历到的节点入栈
- 遍历完后,进行出栈操作,同时打印出栈元素
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | public class Demo1 { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); linkedList.traverseNode(); System.out.println(); //创建学生节点,并插入链表 StudentNode student1 = new StudentNode( 1 , "Nyima" ); StudentNode student3 = new StudentNode( 3 , "Lulu" ); linkedList.addNode(student1); linkedList.addNode(student3); linkedList.traverseNode(); System.out.println(); //按id大小插入 System.out.println( "有序插入" ); StudentNode student2 = new StudentNode( 0 , "Wenwen" ); linkedList.addByOrder(student2); linkedList.traverseNode(); System.out.println(); //按id修改学生信息 System.out.println( "修改学生信息" ); student2 = new StudentNode( 1 , "Hulu" ); linkedList.changeNode(student2); linkedList.traverseNode(); System.out.println(); //根据id删除学生信息 System.out.println( "删除学生信息" ); student2 = new StudentNode( 1 , "Hulu" ); linkedList.deleteNode(student2); linkedList.traverseNode(); System.out.println(); //获得倒数第几个节点 System.out.println( "获得倒数节点" ); System.out.println(linkedList.getStuByRec( 2 )); System.out.println(); //翻转链表 System.out.println( "翻转链表" ); LinkedList newLinkedList = linkedList.reverseList(); newLinkedList.traverseNode(); System.out.println(); //倒叙遍历链表 System.out.println( "倒序遍历链表" ); newLinkedList.reverseTraverse(); } } /** * 创建链表 */ class LinkedList { //头节点,防止被修改,设置为私有的 private StudentNode head = new StudentNode( 0 , "" ); /** * 添加节点 * @param node 要添加的节点 */ public void addNode(StudentNode node) { //因为头节点不能被修改,所以创建一个辅助节点 StudentNode temp = head; //找到最后一个节点 while ( true ) { //temp是尾节点就停止循环 if (temp.next == null ) { break ; } //不是尾结点就向后移动 temp = temp.next; } //现在temp是尾节点了,再次插入 temp.next = node; } /** * 遍历链表 */ public void traverseNode() { System.out.println( "开始遍历链表" ); if (head.next == null ) { System.out.println( "链表为空" ); } //创建辅助节点 StudentNode temp = head.next; while ( true ) { //遍历完成就停止循环 if (temp == null ) { break ; } System.out.println(temp); temp = temp.next; } } /** * 按id顺序插入节点 * @param node */ public void addByOrder(StudentNode node) { //如果没有首节点,就直接插入 if (head.next == null ) { head.next = node; return ; } //辅助节点,用于找到插入位置和插入操作 StudentNode temp = head; //节点的下一个节点存在,且它的id小于要插入节点的id,就继续下移 while (temp.next!= null && temp.next.id < node.id) { temp = temp.next; } //如果temp的下一个节点存在,则执行该操作 //且插入操作,顺序不能换 if (temp.next != null ) { node.next = temp.next; } temp.next = node; } /** * 根据id来修改节点信息 * @param node 修改信息的节点 */ public void changeNode(StudentNode node) { if (head == null ) { System.out.println( "链表为空,请先加入该学生信息" ); return ; } StudentNode temp = head; //遍历链表,找到要修改的节点 while (temp.next!= null && temp.id != node.id) { temp = temp.next; } //如果temp已经是最后一个节点,判断id是否相等 if (temp.id != node.id) { System.out.println( "未找到该学生的信息,请先创建该学生的信息" ); return ; } //修改学生信息 temp.name = node.name; } /** * 根据id删除节点 * @param node 要删除的节点 */ public void deleteNode(StudentNode node) { if (head.next == null ) { System.out.println( "链表为空" ); return ; } StudentNode temp = head.next; //遍历链表,找到要删除的节点 if (temp.next!= null && temp.next.id!=node.id) { temp = temp.next; } //判断最后一个节点的是否要删除的节点 if (temp.next.id != node.id) { System.out.println( "请先插入该学生信息" ); return ; } //删除该节点 temp.next = temp.next.next; } /** * 得到倒数的节点 * @param index 倒数第几个数 * @return */ public StudentNode getStuByRec( int index) { if (head.next == null ) { System.out.println( "链表为空!" ); } StudentNode temp = head.next; //用户记录链表长度,因为head.next不为空,此时已经有一个节点了 //所以length初始化为1 int length = 1 ; while (temp.next != null ) { temp = temp.next; length++; } if (length < index) { throw new RuntimeException( "链表越界" ); } temp = head.next; for ( int i = 0 ; i<length-index; i++) { temp = temp.next; } return temp; } /** * 翻转链表 * @return 反转后的链表 */ public LinkedList reverseList() { //链表为空或者只有一个节点,无需翻转 if (head.next == null || head.next.next == null ) { System.out.println( "无需翻转" ); } LinkedList newLinkedList = new LinkedList(); //给新链表创建新的头结点 newLinkedList.head = new StudentNode( 0 , "" ); //用于保存正在遍历的节点 StudentNode temp = head.next; //用于保存正在遍历节点的下一个节点 StudentNode nextNode = temp.next; while ( true ) { //插入新链表 temp.next = newLinkedList.head.next; newLinkedList.head.next = temp; //移动到下一个节点 temp = nextNode; nextNode = nextNode.next; if (temp.next == null ) { //插入最后一个节点 temp.next = newLinkedList.head.next; newLinkedList.head.next = temp; head.next = null ; return newLinkedList; } } } public void reverseTraverse() { if (head == null ) { System.out.println( "链表为空" ); } StudentNode temp = head.next; //创建栈,用于存放遍历到的节点 Stack<StudentNode> stack = new Stack<>(); while (temp != null ) { stack.push(temp); temp = temp.next; } while (!stack.isEmpty()) { System.out.println(stack.pop()); } } } /** * 定义节点 */ class StudentNode { int id; String name; //用于保存下一个节点的地址 StudentNode next; public StudentNode( int id, String name) { this .id = id; this .name = name; } @Override public String toString() { return "StudentNode{" + "id=" + id + ", name='" + name + '\ '' + '}' ; } } |
本文来自博客园,作者:腹白,转载请注明原文链接:https://www.cnblogs.com/wyh518/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?