反转链表类型题

leetcode 206

思路

  • 迭代:遍历链表,将当前节点的next指向前一个节点,中间过程需要保存前一个节点和下一个节点(反转后最后一个节点指向空)

    时间复杂度O(n),空间复杂度O(1)

  • 递归:将反转整个链表分解为两个子问题(反转head和反转head以外的其它节点),往下分解为只剩下一个节点(不需要反转),然后反转两个节点一直往上归并直至反转完整个链表

    时间复杂度O(n),空间复杂度O(n)(递归调用的栈空间)

代码

迭代

class Solution {
	public:
		ListNode *reverseList(ListNode *head) {
			ListNode *pre = nullptr;
			ListNode *cur = head;
            ListNode *next;
			while (cur) {
                next = cur->next;
				cur->next = pre;
				pre = cur;
				cur = next;
			}
			return pre;
		}
};

递归

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //递归终止条件:当前节点为空或者只有一个节点
        if(head == nullptr || head->next == nullptr){
            return head;
        }
        //递的过程
        ListNode *newHead = reverseList(head->next);
        //归的过程
        head->next->next = head;
        head->next = nullptr; //最后的节点指向空 
        return newHead;
    }
};

leetcode 92

思路

  • 方法一:将需要反转的链表区间反转后与不需要反转的区间拼接起来

    时间复杂度O(n),空间复杂度O(1)

  • 方法二:头插法,遍历反转区间,将当前节点放到反转起始的位置,直至反转区间节点反转完

    时间复杂度O(n),空间复杂度O(1)

代码

方法一

class Solution {
private:
    ListNode *reverseList(ListNode *head){
        if(!head || !head->next){
            return head;
        }
        ListNode *newHead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newHead;
    }
public:
    ListNode *reverseBetween(ListNode *head, int left, int right) {
			ListNode *dummyNode = new ListNode(-1);
			dummyNode->next = head;

            ListNode *pre = dummyNode;  //走left-1步找到left前一个节点
			for (int i = 0; i < left - 1; i++) {
				pre = pre->next;
			}
            
            ListNode *rightNode = pre; //再走right - left + 1找到right
            for(int i = 0;i < right - left + 1;i++){
                rightNode = rightNode->next;
            }

			ListNode *leftNode = pre->next; //记录left前一个
            ListNode *subNode = rightNode->next; //记录right下一个
            
            rightNode->next = nullptr;//截断中间区域

            ListNode *midNode = reverseList(leftNode); //反转中间区域

            //重新连接
            pre->next = midNode;
            leftNode->next = subNode;
			return dummyNode->next;
		}
};

方法二

class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int left, int right) {
			//头插法
			ListNode *dummyNode = new ListNode(-1);
			dummyNode->next = head;

            ListNode *pre = dummyNode;
			for (int i = 0; i < left - 1; i++) {
				pre = pre->next;
			}

			ListNode *cur = pre->next;
			ListNode *next;

			for (int i = 0; i < right - left; i++) {
				next = cur->next;
				cur->next = next->next;
				next->next = pre->next;
				pre->next = next;
			}
			return dummyNode->next;
		}
};

改编题目

题目描述

给定单链表的头指针head,两个整数left和right其中left < right,请你反转从1到left,以及位置right到末尾节点,返回反转后的链表

注:链表长度>=2且left和right范围不超过链表本身

示例

输入

[3,4,5,6,7,8],3,5

输出

[5,4,3,6,8,7]

思路

直接在leetcode92的基础上反转两次[1,left] [right,tail]

代码

class Solution {
	private:
		ListNode *reverseBetween(LisNode *head, int left, int right) {
			//头插法
			ListNode *dummyNode = new ListNode(-1);
			dummyNode->next = head;

			for (int i = 0; i < left - 1; i++) {
				pre = pre->next;
			}

			ListNode *cur = pre->next;
			ListNode *next;

			for (int i = 0; i < right - left; i++) {
				next = cur->next;
				cur->next = next->next;
				next->next = pre->next;
				pre->next = next;
			}
			return dummyNode->next;
		}
	public:
		ListNode *reverseList(listNode *head, int left, int right) {
			ListNode *temp = head;
			int tail = 0;
			while (temp) {
				temp = temp->next;
				tail++;
			}
			//先反转左半边
			head = reverseBetween(head, 1, left);
			//再翻转右半边
			head = reverseBetween(head, right, tail);
			return head;
		}
};
posted @ 2021-07-17 22:03  简约的信仰  阅读(146)  评论(0编辑  收藏  举报