206.反转链表
1.方法一:通过头插法遍历链表,再插入新链表中
Java代码实现:
public ListNode reverseList(ListNode head) {
if (head == null) return null;
//1.创建新链表,用来存放反转链表
ListNode reverse = null;
//2.遍历链表元素,将链表中的元素添加到数组
for (ListNode p = head; p != null; p = p.next) {
//3.头插入代码
reverse = new ListNode(p.val, reverse);
}
return reverse;
}
C代码实现:
struct ListNode* reverseList(struct ListNode* head){
//1.如果链表为空,返回空链表
if(head == NULL) return NULL;
//2.创建新链表,将反转的链表装此链表中
struct ListNode *newList = NULL;
//3.遍历链表,实现链表反转
for (struct ListNode *p = head; p != NULL; p = p->next){
//4.头插法实现链表反转
struct ListNode *node = (struct ListNode*) malloc(sizeof(struct ListNode));
node->val = p->val;
node->next = newList;
newList = node;
}
return newList;
}
2.方法二:递归遍历
- 递归算法的一般步骤
- 定义基本情况:确定一个或多个基本情况,即无需再次递归求解的情况。这是递归的终止条件。
- 缩小问题规模:将原始问题转化为一个或多个规模较小的子问题。通常,这一步可以通过参数的改变、数组的切割等方式实现。
- 调用自身:在解决子问题时,调用自身来求解更小规模的同种问题。这就是递归的核心思想。
- 组合结果:将子问题的解组合起来,得到原始问题的解。
在实现递归算法时,需要注意确保基本情况能够被满足,并且每次递归都能使问题规模减小。同时,递归算法要注意避免出现无限递归的情况,因此通常需要设置递归深度限制或者其他终止条件。
Java代码实现:
public ListNode reverseList2(ListNode p){
//递归结束的条件:
//1.如果链表为空 2.遍历后的下一个节点为空,即链表已经遍历完成
if (p == null || p.next == null){
return p;//最后节点
}
//2.进行递归遍历
ListNode last = reverseList2(p.next);
//3.遍历结果进行处理
p.next.next = p;
p.next = null;
return last;
}
- 以反转链表[1,2,3,4,5]进行递归举例
上图来自黑马的2023新版数据结构与算法Java视频教程(上篇)!!!
last变量的解释:
在这段代码中,last
是一个临时变量,用于存储递归调用返回的反转后的子链表的头节点。具体来说,last
代表的是递归调用 reverseList2(p.next)
的返回值,即以当前节点的下一个节点为头节点的子链表的反转结果。我们将 last
的作用是为了保留这个反转后的子链表的头节点,以便将其与当前节点 p
进行连接。在递归的最外层调用中,当递归结束后,last
将存储的是整个链表反转后的头节点。在递归的每一层调用中,last
存储的是当前层递归调用返回的子链表反转后的头节点。通过这种方式,我们可以在每一次递归调用时保存子链表反转后的头节点,并将其与当前节点进行连接操作。因此,last
的作用是临时存储递归调用返回的子链表反转的结果,以便正确地进行链表节点的连接。
C代码实现:
//方法二:递归遍历
Node* reverseList2(Node *p){
//1.递归终止条件
if(p == NULL || p->next == NULL) return p;
//2.进行递归遍历
Node *last = reverseList2(p->next);
//3.对遍历结果进行操作
p->next->next = p;
p->next = NULL;
return last;
}
3.方法三:双指针法
Java代码实现
public ListNode reverseList3(ListNode oldNode){
//ListNode oldNode:代表旧链表的头指针
//ListNode newNode:代表新链表的头指针
//ListNode oldNode2:代表旧链表的头指针的下一个节点
//1.当链表为空,或者链表只有一个元素时,不需要进行反转
if (oldNode == null || oldNode.next == null) return oldNode;
//2.定义节点
ListNode oldNode2 = oldNode.next;
ListNode newNode = oldNode;
//3.反转操作
while (oldNode2 != null){
oldNode.next = oldNode2.next;//将要反转的节点与链表断开
oldNode2.next = newNode;//将反转的节点接到头部
newNode = oldNode2;//重新将新节点指针放在链表头部
oldNode2 = oldNode.next;
}
return newNode;
}
C代码实现
struct ListNode* reverseList3(struct ListNode* oldNode){
// ListNode* oldNode: 代表旧链表的头指针
// ListNode* newNode: 代表新链表的头指针
// ListNode* oldNode2: 代表旧链表的头指针的下一个节点
// 1. 当链表为空,或者链表只有一个元素时,不需要进行反转
if (oldNode == NULL || oldNode->next == NULL) return oldNode;
// 2. 定义节点
struct ListNode* oldNode2 = oldNode->next;
struct ListNode* newNode = oldNode;
// 3. 反转操作
while (oldNode2 != NULL){
oldNode->next = oldNode2->next; // 将要反转的节点与链表断开
oldNode2->next = newNode; // 将反转的节点接到头部
newNode = oldNode2; // 重新将新节点指针放在链表头部
oldNode2 = oldNode->next;
}
return newNode;
}