反转链表
1 从尾到头打印链表内容
问题描述
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
问题分类
问题分析
1.1 从尾到头打印链表内容——辅助栈
策略选择
- 数据结构:链表、栈、数组
- 算法思想:枚举法
- 可以利用栈的先进后出特性。
算法设计
- 入栈: 遍历链表,将各节点值 push 入栈。(Python 使用 append() 方法,Java借助 LinkedList 的addLast()方法)。
- 出栈: 将各节点值 pop 出栈,存储于数组并返回。(Python 直接返回 stack 的倒序列表,Java 新建一个数组,通过 popLast() 方法将各元素存入数组,实现倒序输出)。
算法分析
算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| vector<int> reversePrint1(ListNode* head) { stack<int> s; vector<int> v; ListNode * temp=head; while(temp){ s.push(temp->val); temp=temp->next; } while(!s.empty()){ v.push_back(s.top()); s.pop(); } return v; }
|
1.2 从尾到头打印链表内容——递归法
策略选择
- 数据结构:链表
- 算法思想:枚举法
- 利用递归后续遍历的特性(子节点在父节点之前除了)
算法设计
- 递推阶段: 每次传入 head.next ,以 head == None(即走过链表尾部节点)为递归终止条件,此时返回空列表 [] 。
- 回溯阶段: 利用 Python 语言特性,递归回溯时每次返回 当前 list + 当前节点值 [head.val] ,即可实现节点的倒序输出。
算法分析
算法实现
1 2 3 4 5 6 7 8 9 10 11
| vector<int> reversePrint(ListNode* head) { vector<int> vec; rp(head,vec); return vec; } void rp(ListNode* head,vector<int>&vec) { if(head==nullptr)return ; rp(head->next,vec); vec.push_back(head->val); return ; }
|
2 反转链表
问题描述
- 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
链接
2.1 反转链表——普通循环反转
策略选择
算法设计
- 在每次循环的时候。到达一个节点。
- 记录本节点的下一个节点
- 记录本节点的上一个几点
- 反转本次节点指向上一个节点
- 复制本层节点= 下一个节点。开始下一次循环。
算法分析
算法实现
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
| ListNode* reverseBetween(ListNode* head, int left, int right) { int i=1; ListNode* node=head; ListNode* last_node=nullptr; ListNode* before_left=nullptr; if(left==1){ before_left=new ListNode(); before_left->next=head; } ListNode* after_node; while(true){ after_node=node->next; if(i==left-1){ before_left=node; } if(i>left && i<=right){ node->next=last_node; } if(i==right){ before_left->next->next = after_node; before_left->next=node; break; } last_node=node; node=after_node; i++; } if(left==1)return before_left->next; return head; }
|
2.2 反转链表——头插法反转
策略选择
算法设计
- 每次循环的时候。到达本节点。将本节点的下一个节点插入到左节点前的下一个节点。从left开始
- temp保留该节点的下一个几点。
- 当前节点指向下一个节点的下一个节点(删除下一个节点)
- temp指向左前节点的下一个节点
- 左前节点指向该节点(插入下一个几点)
算法分析
算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // 头插法 ListNode* reverseBetween2(ListNode* head, int left, int right) { ListNode* before_head = new ListNode(); before_head->next=head; ListNode* before_left; ListNode* node=before_head; ListNode* temp; for(int i=0;i<=right;i++){ if(i==left-1)before_left=node; if(i>=left && i<right){ temp = node->next; node->next = temp->next; temp->next=before_left->next; before_left->next=temp; } else{ node=node->next; } } return before_head->next; }
|
链表反转——递归法
策略选择
- 递归法
- 等价于使用栈保存了节点的路径。不会在反转后回不到过去的节点
算法设计
递归
- 当到右节点,使用全局变量记录右节点和右节点的下一个节点。
- 当在左右之间时,直接使自身的下一个节点指向自己。
- 当到达左节点时。与有节点和有节点的下一个反转。
递归的参数
递归的返回值
递归的执行
递归的终止条件
递归前的处理和递归后的处理。
算法分析
算法实现
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
| ListNode* reverseBetween(ListNode* head, int left, int right) { ListNode* before_head = new ListNode(); before_head->next=head; dfs(before_head,left,right,0); return before_head->next; } ListNode* right; ListNode* right_next; void dfs(ListNode* head,int left,int right,int i){ if(i>right)return ; dfs(head->next,left,right,i+1); if(i>=left && i<right){ head->next->next=head; } if(i==right){ this->right=head; this->right_next=head->next; } if(i==left-1){ head->next->next=this->right_next; head->next=this->right; } return ; }
|