随插随删的链表,Java模拟链表,操作链表,反转链表,合并两个链表并排序
1.认识链表
链表相较于数组,除了基本的数据域,还增加了指针域用于构建链式存储数据。
链表的每一个节点包含此节点的数据+指向下一节点地址的指针。
对链表中的节点进行增加和删除时,只需要对上一节点指针地址进行修改,其他节点无需变动。所以链表增加、删除效率高,查找效率低。
对链表指针域进行反向连接,可形成双向链表或者循环链表。
![]() |
---|
2.Java模拟链表
初始化链表时,定义一个头指针head(-1,head.next=null)用它来判断链表是否为NULL。
这个头指针不是节点,打印链表时也不能带它,他只是个辅助指针。
向链表中添加第一个节点的情况
![]() |
---|
向链表中两个节点间添加节点情况
![]() |
---|
删除链表中一个值为x的节点
![]() |
---|
通过ListNode类模拟链表
class ListNode{
int data;
ListNode next;
static ListNode head = new ListNode(-1); //head.next=null
ListNode(int val){
this.data=val;
}
//将y插入到x的前面
void insert(int x,int y){
ListNode node = head;
ListNode newNode = new ListNode(y);
int sign = 0;
while(node.next!=null){
if(node.next.data==x){
newNode.next=node.next;
node.next=newNode;
sign=1;
break;
}
node = node.next;
}
if(sign==0){
node.next=newNode;
}
}
//删除data=x的节点
void delete(int x){
ListNode node =head;
while(node.next!=null){
if(node.next.data==x){
node.next = node.next.next;
break;
}
node=node.next;
}
}
}
带输入输出条件来操作链表
输入: 3 操作次数i=3
insert 0 1 行为:在节点值为0的前面插入值为1的节点
insert 0 2
insert 0 3
输出: 1 2 3 打印链表
代码:
import java.util.Scanner;
/**
* 通过ListNode类模拟链表
*/
public class ListNode1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入操作次数i");
int i = sc.nextInt();
ListNode ln = new ListNode();
for(;i>=0;i--){
String str = sc.nextLine();
String[] strArr = str.split(" ");
switch(strArr[0]){
case "insert":
ln.insert(Integer.parseInt(strArr[1]), Integer.parseInt(strArr[2]));
break;
case "delete":
ln.delete(Integer.parseInt(strArr[1]));
break;
}
}
ListNode node = ln.head;
if(node.next==null){
System.out.println("NULL");
return;
}
while(node.next!=null){
System.out.print(node.next.data+" ");
node=node.next;
}
}
}
class ListNode{
int data;
ListNode next;
static ListNode head = new ListNode(-1); //head.next=null
ListNode(){}
ListNode(int val){
this.data=val;
}
//将y插入到x的前面
void insert(int x,int y){
ListNode node = head;
ListNode newNode = new ListNode(y);
int sign = 0;
while(node.next!=null){
if(node.next.data==x){
newNode.next=node.next;
node.next=newNode;
sign=1;
break;
}
node = node.next;
}
if(sign==0){
node.next=newNode;
}
}
//删除data=x的节点
void delete(int x){
ListNode node =head;
while(node.next!=null){
if(node.next.data==x){
node.next = node.next.next;
break;
}
node=node.next;
}
}
}
3.Java反转链表
输入: {1,2,3}
输出: {3,2,1}
方法一
思路:
-
先通过createListNode方法将输入数据用链表串起来
-
然后通过ReverseList方法,传入链表真正的第一个节点head(ln.head.next)
-
令新链表的头结点newNode=null,每访问原链表节点都让它成为新链表的头结点(head.next=newNode,newNode=head)
-
需要注意,在摘下原链表的每一个节点时,保存该节点的下一个节点(temp=head.next),在head成为新链表的头节点后,将原链表的头节点设为temp(head=temp)
代码
import java.util.Scanner;
public class reListNode2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入初始链表,例如{1,2,3}");
String str = sc.nextLine();
str = str.substring(1, str.length()-1);
String[] strArr = str.split(",");
ListNode ln = new ListNode();
createListNode(ln,strArr);
ListNode head =ln.head.next;
ReverseList(head);
}
//反转链表
private static void ReverseList(ListNode head) {
ListNode newNode = null;
while(head!=null){
ListNode temp = head.next;
head.next=newNode;
newNode=head;
head=temp;
}
//反转后打印链表{3,2,1}
System.out.print("{");
while(newNode!=null){
if(!(newNode.next==null)){
System.out.print(newNode.data+",");
newNode=newNode.next;
}else{
System.out.print(newNode.data);
newNode=newNode.next;
}
}
System.out.print("}");
}
//构造链表
private static void createListNode(ListNode ln, String[] strArr) {
if(strArr[0].equals("")){
}else{
ListNode node = ln.head;
int i=0;
while(i<strArr.length){
ListNode newNode = new ListNode(Integer.parseInt(strArr[i]));
node.next=newNode;
node=newNode;
i++;
}
}
}
}
class ListNode{
int data;
ListNode next;
static ListNode head = new ListNode(-1); //head.next=null
ListNode(){}
ListNode(int val){
this.data=val;
}
}
方法二:递归
![]() |
---|
public ListNode ReverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
//保存当前节点的下一个节点
ListNode next = head.next;
//从当前节点的下一个节点开始递归调用
ListNode reverse = ReverseList(next);
//reverse是反转之后的链表,因为函数reverseList
// 表示的是对链表的反转,所以反转完之后next肯定
// 是链表reverse的尾结点,然后我们再把当前节点
//head挂到next节点的后面就完成了链表的反转。
next.next = head;
//head相当于变成了尾结点,尾结点都是为空的,
head.next = null;
return reverse;
}
方法三:使用栈
原理就是把链表节点一个个入栈,当全部入栈完之后再一个个出栈,出栈的时候在把出栈的结点串成一个新的链表。
import java.util.Stack;
public class Solution {
public ListNode ReverseList(ListNode head) {
while(head!=null){
stack.push(head);
head=head.next;
}
if(stack.isEmpty())
return null; //证明链表中根本没元素
ListNode node = stack.pop();
ListNode dummy = node;
//栈中的结点全部出栈,然后重新连成一个新的链表
while (!stack.isEmpty()) {
ListNode tempNode = stack.pop();
node.next=tempNode;
node = node.next;
}
//最后一个结点就是反转前的头结点,一定要让他的next
//等于空,否则会构成环
node.next=null;
return dummy;
}
}
上面的情况适用于牛客网刷题,链表事先是给你构造好了的。
如果没给你构造链表的情况下,根据输入的数据也可以获取到翻转后的链表。
//例如输入的的是{1,2,3}
import java.util.Scanner;
import java.util.Stack;
/**
* 通过栈反转链表
*/
public class reListNode3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入初始链表,例如{1,2,3}");
String str = sc.nextLine();
str = str.substring(1, str.length()-1);
String[] strArr = str.split(",");
ListNode head = new ListNode(Integer.parseInt(strArr[0])); //这里只是新建了个值为1的链表节点,该节点next=null
System.out.print("{");
ListNode reList=ReverseList(head,strArr);
while(reList!=null){
if(reList.next!=null){
System.out.print(reList.data+",");
}else{
System.out.print(reList.data);
}
reList = reList.next;
}
System.out.print("}");
}
//反转链表
private static ListNode ReverseList(ListNode head,String[] strArr) {
Stack stack = new Stack<ListNode>();
stack.push(head);
int i=1;
while(head!=null&&i<strArr.length){
head = new ListNode(Integer.parseInt(strArr[i]));
stack.push(head);
i++;
}
if(stack.isEmpty()) return null;
ListNode node = (ListNode)stack.pop();
ListNode dummy = node;
while(!stack.isEmpty()){
ListNode temp = (ListNode)stack.pop();
node.next=temp;
node=node.next;
}
node.next=null;
return dummy;
}
}
class ListNode{
int data;
ListNode next;
ListNode(){}
ListNode(int val){
this.data=val;
}
}
在没有构造链表的情况下直接输出翻转后的链表,相当于根据输入的数据倒着构造一个链表
4.合并两个链表并排序
输入:
{1,3,5},{2,4,6}
输出:
{1,2,3,4,5,6}
输入:
{},{}
输出:
{}
思路一:
- 创建最终得到的链表node的哑节点node(-1),和一个存放node的dummy节点
- 当
list1
与list2
都不为null
时循环 - 哪个的
val
小哪个赋给哑节点的next
称为最终链表的头节点,node虚拟结点后移,该val所在的节点后移。 - 退出循环后,哪个
list
不为空,哪个结点(包括剩下的)给node的next
- 最终返回dummy.next,因为dummy是最开始的node(-1)是个哑节点,不应该被输出
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode node = new ListNode(-1);
ListNode dummy = node;
while(list1!=null&&list2!=null){
if(list1.val>=list2.val){
node.next=list2;
list2=list2.next;
node=node.next;
}else{
node.next=list1;
list1=list1.next;
node=node.next;
}
}
if(list1==null){
node.next=list2;
}else{
node.next=list1;
}
return dummy.next;
}
}
思路二:递归
-
先说终止条件:如果list1和list2其中有一个为空,返回另一个链表
-
比较list1.val和list2.val,将较小的listX.next与merge后的表头连接即:
例如:list1.next = Merge(list1.next,list2);
每次返回排序好的链表头
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}
else if(list2==null){
return list1;
}
if(list2.val>list1.val){
list1.next=Merge(list1.next,list2);
return list1;
}else{
list2.next=Merge(list1,list2.next);
return list2;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!