数据结构和算法-常见算法题
参考:
链表反转:
https://blog.csdn.net/weixin_40807247/article/details/91435275
https://blog.csdn.net/qq_33958946/article/details/84326965
https://blog.csdn.net/qq_37117521/article/details/80808631
链表反转
理解单链表的反转(java实现)
遍历法
遍历法就是在链表遍历的过程中将指针顺序置换
//如何实现链表的反转
public static Node ReverseIteratively(Node head){
Node pre = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
依旧是1->2->3->4
准备两个空结点 pre用来保存先前结点、next用来做临时变量
在头结点node遍历的时候此时为1结点
next = 1结点.next(2结点)
1结点.next=pre(null)
pre = 1结点
node = 2结点
进行下一次循环node=2结点
next = 2结点.next(3结点)
2结点.next=pre(1结点)=>即完成2->1
pre = 2结点
node = 3结点
进行循环…
递归法
总体来说,递归法是从最后一个Node开始,在弹栈的过程中将指针顺序置换的。
递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场。
我们来看是怎样的一个递归过程:1->2->3->4
程序到达Node newHead = reverse(head.next);时进入递归
我们假设此时递归到了3结点,此时head=3结点,temp=3结点.next(实际上是4结点)
执行Node newHead = reverse(head.next);传入的head.next是4结点,返回的newHead是4结点。
接下来就是弹栈过程了
程序继续执行 temp.next = head就相当于4->3
head.next = null 即把3结点指向4结点的指针断掉。
返回新链表的头结点newHead
//递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场
public Node reverse(Node head) {
//递归
if (null == head ||null == head.next)
return head;
Node temp = head.next;
Node newHead = reverse(head.next);
temp.next = head;
head.next = null;
return newHead;
}
@Test
public void testCase() {
LinkedList list = new LinkedList();
System.out.println("测试头插法");
list.addNodeHead(4);
list.addNodeHead(3);
list.display();
list.addNodeHead(2);
list.addNodeHead(1);
/*list.display();
list.addNodeTail(4);
list.display();
System.out.println("测试某个位置删除法");
list.deleteNode(3);
list.display();
System.out.println("测试删除重复数据前");
list.addNodeHead(1);
list.display();
System.out.println("测试删除重复数据");
list.deleteDuplecate1(list.head);
list.display();
System.out.println("测试删除重复数据1");
list.addNodeIndex(2,1);
list.display();
list.deleteDuplecate(list.head);*/
list.display();
System.out.println("链表的反转");
//Node node = LinkedList.ReverseIteratively(list.head);
Node node1 = list.reverse(list.head);
while (node1 != null){
System.out.println(node1.data);
node1 = node1.next;
}
}
java经典面试题:单链表反转问题详解(含递归法)
java经典面试题:单链表反转问题,有两种方法,一种为循环遍历法,一种递归法。
1、循环遍历法
首先设置三个节点,把当前节点的下一节点指向它前面的节点,此时你会发现指针链会断,所以要先把它后面一个节点用nextNode保存下来,之后把节点向后移动遍历即可。
代码如下:
//定义单链表节点类
public class ListNode {
int value;
ListNode next;
public ListNode() {
this.value=0;
this.next=null;
}
public ListNode(int value) {
this.value=value;
}
}
//定义链表反转方法
public ListNode reverseList(ListNode root) {
if(root==null)return root;
ListNode newhead=null; //反转后的新链表头结点
ListNode preNode = null; //前一节点
ListNode nextNode=null; //下一节点
ListNode curNode=root; //当前节点
while(curNode.next!=null) {
nextNode=curNode.next; //保存当前节点的下一节点
curNode.next=preNode; //使当前节点指向前一节点
preNode=curNode; //把前一节点往后移
curNode=nextNode; //把当前节点往后移
}
newhead=curNode;
return newhead;
}
2、递归法
如果当前节点或者当前节点的下一节点为空,直接返回该节点;
否则使该节点的后一节点指向该节点,以此递归。
public BinaryNode reverseList2(BinaryNode root) {
if(root==null||root.next==null)return root;
BinaryNode nextNode=root.next; //得到当前节点的下一节点
root.next=null; //打断当前指针链
BinaryNode re=reverseList2(nextNode); //每次递归下一节点进行反转
nextNode.next=root; //反转指针域
return re;
}
使用递归反转链表
链表反转:
(图1)
把问题规模减小,并且减小的量为1
(图2)
假设我们的程序能够正常的反转:则反转后为
(图3)
反转后,1元素并没有任何操作,所以反转1的next仍然指向2,
(图4)
假设2开头的链表已经反转成功,接下来只要将2的next指向1,
(图5)
而1的next指向null即可。
(图6)
看似复杂的问题,把如此多的指针反过来指,其实只要将两个节点反过来即可。
代码如下:
package com.sise.recursion; import java.util.ArrayList; import java.util.Arrays; public class LinkedListReverser { /* * 反转一个链表 * head为待反转链表的头结点 * @return 反转后的链表头结点,当然该链表也是以null结尾 */ public Node reverseLinkedList(Node head) { /* *特殊处理 */ // //空链表,sise==0 // if(head==null){ // return null; // } // //只有一个结点的时候,size==1 // if(head.getNext()==null){ // return head; // } //把上两个特殊情况合起来 if(head==null||head.getNext()==null){ return head; } //假设函数能够反转链表,返回头结点 //---------------此处head有可能是null,head。getNext()有可能是null----------- Node newHead=reverseLinkedList(head.getNext()); //此时如图4状态,1的getNext就是第二个结点2, //把第二结点2的next指向head则实现把2的指针指向1,如图5 //------------此处的getNext()有可能是null------ head.getNext().setNext(head); head.setNext(null);//最后指向null,如图6 return newHead; } public static void main(String[] args) { LinkedListCreator creator=new LinkedListCreator(); LinkedListReverser reverser=new LinkedListReverser(); ArrayList arrayList=new ArrayList<>(); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(arrayList)) ); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1))) ); Node.printLinkedList( reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1,2,3,4,5))) ); } }
package com.sise.recursion; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class LinkedListCreator { /* * 创建一个链表 * @return 链表的头结点,返回链表的最后一个结点的getNext()==null. */ public Node createLinkedList(List<Integer> data){ //假设传入空的List if(data.isEmpty()){ return null; } //取出传入数据的第一个结点 Node firstNode=new Node(data.get(0)); //取走一个元素后,从第二个元素创建一个链表, //因为返回的是Node,所以用Node来接收 //假设传入来的List有一个元素,则走到这里时sublist传入的两个参数相等 //但是sublist函数的定义可以看到fromIndex==toIndex时,返回null /* * <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is */ //与我们期望返回值一致 // Node headOfSublistNode= // createLinkedList(data.subList(1, data.size())); // //第一个结点的next指向规模缩小的链表返回来的头结点 // firstNode.setNext(headOfSublistNode); //上面两行代码清理成如下代码 firstNode.setNext(createLinkedList(data.subList(1, data.size()))); return firstNode; } public static void main(String[] args) { LinkedListCreator creator=new LinkedListCreator(); ArrayList arrayList=new ArrayList<>(); Node.printLinkedList( creator.createLinkedList(arrayList) ); Node.printLinkedList( creator.createLinkedList(Arrays.asList(1)) ); Node.printLinkedList( creator.createLinkedList(Arrays.asList(1,2,3,4,5)) ); } }
package com.sise.recursion; public class Node { private final int value;//用户定义之后就不能修改 private Node next; public Node(int value){ this.value=value; this.next=null;//这样建立出来的结点都是单点Node } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public int getValue() { return value; } //打印函数 public static void printLinkedList(Node head) { while(head!=null){ System.out.print(head.getValue());; System.out.print(" "); head=head.getNext(); } System.out.println(); } }
运行结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用