链表 -->总结

1. 头插法尾插法

 //头插法是insert反序,只需要头指针
 ListNode* head_insert(vector<int>& nums)
 {
	 ListNode* head = NULL;
	 for (int a : nums)
	 {
		 ListNode* p = new ListNode(a);
		 p->next = head;
		 head = p;
	 }
	 return head;
}
 //尾插法是正序push_back, 需要头指针和尾指针。
 ListNode* tail_insert(vector<int>& nums)
 {
	 ListNode* head = NULL, *tail = head;
	 for (int a : nums)
	 {
		 ListNode* p = new ListNode(a);
		 if (head == NULL) tail = head = p;
		 else
		 {
			 tail->next = p;
			 tail = p;
		 }
	 }
	 return head;
 }
1.1 链表相加,这道题注意大数相加的模式写法。
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) 链表均是数字大反序表示,要求返回和的反序。
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
class Solution {
 public:
	 ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
		 ListNode* head = NULL, *tail = head;
		 int c = 0;
		 while (l1 || l2)
		 {
			 int a = l1 == NULL ? 0 : l1->val;
			 int b = l2 == NULL ? 0 : l2->val;
			 c = a + b + c;
			 ListNode* p = new ListNode(c % 10);
			 if (head == NULL)
			 {
				 head = tail = p;
			 }
			 else
			 {
				 p->next = head;
				 head = p;
			 }
			 c = c / 10;
			 if (l1) l1 = l1->next;
			 if (l2) l2 = l2->next;
		 }
		 if (c) {
			 ListNode* p = new ListNode(c % 10);
			 if (head == NULL)
			 {
				 head = tail = p;
			 }
			 else
			 {
				 p->next = head;
				 head = p;
			 }
		 }
		 return head;
	 }
 };

建立链表就用尾插法或者头插法。如果没使用new方式建立,就是在原链表上改变。但要注意尾指针的next赋值NULL,否则原地改变的话,tail->next可能还指向某节点。

1.2 合并两个排序链表: 21. Merge Two Sorted Lists
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
 class Solution {
 public:
	 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
		 ListNode* head = NULL, *tail = head, *p = l1, *q = l2;
		 while (p&&q)//先建立公共部分
		 {
			 if (p->val < q->val)
			 {
				 if (head == NULL) tail = head = p;
				 else {
					 tail->next = p;
					 tail = p;
				 }
				 p = p->next;
			 }
			 else
			 {
				 if (head == NULL) tail = head = q;
				 else {
					 tail->next = q;
					 tail = q;
				 }
				 q = q->next;
			 }
		 }
		 if (p)//在把长的部分连上,注意一个链表为空另一个不空的情况。
		 {
			 if (head == NULL) tail = head = q;
			 else {
				 tail->next = q;
				 tail = q;
			 }
		 }
		 if (q)
		 {
			 if (head == NULL) tail = head = q;
			 else {
				 tail->next = q;
				 tail = q;
			 }
		 }
		 return head;
	 }
 };
1.3 24. Swap Nodes in Pairs
Given 1->2->3->4, you should return the list as 2->1->4->3.
class Solution {
 public://注意奇数长度和偶数长度区别。
	 ListNode* swapPairs(ListNode* node) {
		 ListNode* head = NULL, *tail = NULL, *p = node;
		 while (p)
		 {
			 ListNode* next = p->next ? p->next->next : NULL; //因为节点指向被改变,所以提前记录下一个节点。
			 if (p->next)
			 {
				 if (head == NULL) tail = head = p->next;
				 else {
					 tail->next = p->next;
					 tail = p->next;
				 }
			 }
			 if (head == NULL) tail = head = p;
			 else {
				 tail->next = p;
				 tail = p;
			 }
			 
			 p = next;
		 }
         if(tail) tail->next=NULL;//注意处理最后一个节点,否则无限循环。
		 return head;
	 }
 };
1.4 92. Reverse Linked List II反转链表的指定位置部分。把反转部分用头插法建立链表,处理好这部分的头尾指针。
Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
class Solution {
 public:
	 ListNode* reverseBetween(ListNode* head, int m, int n) {
		 ListNode*dummy = new ListNode(0), *pre = dummy;
		 dummy->next = head;
		 for (int i = 0; i < m - 1; i++) pre = pre->next;
		 //头插法
		 ListNode* phead = NULL, *tail = NULL, *p = pre->next;
		 for (int i = 0; i < n - m + 1 && p; i++)
		 {
			 ListNode* next = p->next;
			 if (phead == NULL) tail = phead = p;
			 else
			 {
				 p->next = phead;
				 phead = p;
			 }
			 p = next;
		 } 
		 pre->next = phead;
		 tail->next = p;
		 return dummy->next;
	 }
 };
1.5 去除重复节点
82. Remove Duplicates from Sorted List II
Example 1:

Input: 1->2->3->3->4->4->5
Output: 1->2->5
class Solution {
 public:
	 ListNode* deleteDuplicates(ListNode* head) {
		 if (!head) return NULL;
		 //尾插法
		 ListNode* phead = NULL, *tail = NULL, *p = head;
		 int last;
		 //分析某个值是否重复,可以把该节点是头节点?尾节点?头节点且尾节点?中间节点讨论。
		 //如果既不与前面相等也不与后面相等就是没重复值。
		 while (p)
		 {
			 bool b;
			 if (p == head && p->next)
			 {
				 if (p->val != p->next->val) b = true;
				 else b = false;
			 }
			 else if (p != head && p->next == NULL)
			 {
				 if (p->val != last) b = true;
				 else b = false;
			 }
			 else if (p == head && p->next == NULL)
			 {
				 b = true;
			 }
			 else
			 {
				 if (p->val != last && p->val != p->next->val) b = true;
				 else b = false;
			 }
			 if (b)
			 {
				 if (phead == NULL) tail = phead = p;
				 else {
					 tail->next = p;
					 tail = p;
				 }
			 }
			 last = p->val;
			 p = p->next;
		 }
		 tail->next = NULL;
		 return phead;
	 }
 };
1.6 86. Partition List

把大于等于x节点放在后面,小于的在前面返回新链表。哈哈涉及到新链表创建还是尾插法或头插法,因为顺序不是反序所以尾插。这用两个链表,一个链表的话还得回头处理大于x的节点。 注意只有>=x和<x的情况判断。

Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
 class Solution {
 public:
	 ListNode* partition(ListNode* head, int x) {
		 if (!head) return head;
		 ListNode* head1 = NULL, *tail1 = NULL, *head2 = NULL, *tail2 = NULL, *p = head;
		 while (p)
		 {
			 ListNode* next = p->next;
			 if (p->val < x)
			 {
				 if (head1 == NULL) tail1 = head1 = p;
				 else
				 {
					 tail1->next = p;
					 tail1 = p;
				 }
			 }
			 else
			 {
				 if (head2 == NULL) tail2 = head2 = p;
				 else
				 {
					 tail2->next = p;
					 tail2 = p;
				 }
			 }
			 p = next;
		 }
		 //建立新链表一定要注意tail->next=NULL,但是要判断尾指针存在。
		 if (tail2) tail2->next = NULL;
		 if (tail1 == NULL) return head2;
		 else
		 {
			 tail1->next = head2;
			 return head1;
		 }
	 }
 };
1.6 重新排列
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

Given 1->2->3->4, reorder it to 1->4->2->3.
Example 2:
Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
//可以统一把节点放在容器里,然后重新制定指针。如果限制空间复杂度可以这样:
//先把len/2后面节点反向,然后插入到前面来。
 class Solution {
 public:
	 void reorderList(ListNode* head) {
		 if (!head) return;
		 int len = 0;
		 ListNode* pre = head, *phead = NULL, *tail = NULL;
		 while (pre)
		 {
			 len++;
			 pre = pre->next;
		 }
		 pre = head;
		 if (len % 2 == 0) len = len / 2 - 1;//偶数个处理到这么多,画图!
		 else len = len / 2;
		 //把后半段反向
		 for (int i = 0; i < len; i++) pre = pre->next;
		 ListNode* p = pre->next;
		 while (p)
		 {
			 ListNode* next = p->next;
			 if (phead == NULL) tail = phead = p;
			 else {
				 p->next = phead;
				 phead = p;
			 }
			 p = next;
		 }
		 if (tail) tail->next = NULL;
		 pre->next = NULL;
	//后半段插到前面来
		 p = head;
		 while (phead)
		 {
			 ListNode* next1 = p->next, *next2 = phead->next;
			 phead->next = p->next;
			 p->next = phead;
			 phead = next2;
			 p = next1;
		 }
	 }
 };

2. 插入排序

 /*破坏掉原来链表构建新的链表,因为插入元素有可能比第一个节点元素大,所以要用辅助节点*/
 class Solution {
 public:
	 ListNode* insertionSortList(ListNode* head) {
		 ListNode* dummy = new ListNode(0);
		 while (head)
		 {
			 ListNode *p = dummy;//从新链表头开始找。
			 ListNode* t = head->next;//先把原链表节点记录下来,因为是要被破坏的。
			 while (p->next && p->next->val < head->val) p = p->next;
			 head->next = p->next;
			 p->next = head;
			 head = t;
		 }
		 return dummy->next;
	 }
 };
  1. 二分法查找
//选择这一种写法
 int binnary_search(vector<int> &nums, int target)
 {
	 int low = 0, high = nums.size() - 1;
	 while (low <= high)
	 {
		 int mid = (low + high) / 2;
		 if (nums[mid] < target) low = mid + 1;
		 else if (nums[mid] > target) high = mid - 1;
		 else return mid;
	 }
	 return -1;
 }
 int binnary_search(vector<int> &nums, int target)
 {
	 int low = 0, high = nums.size();
	 while (low < high)
	 {
		 int mid = (low + high) / 2;
		 if (nums[mid] < target) low = mid + 1;
		 else if (nums[mid] > target) high = mid;
		 else return mid;
	 }
	 return -1;
 }
33. Search in Rotated Sorted Array

排序好的数组分成两段,再让查找target位置。
. Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
//找出第一个下降点,则下降点左边是大值区间,下降点以及右边是小值区间

class Solution {
 public:
	 int search(vector<int> nums, int target) {
		 if (nums.size() == 0) return -1;
		 int pos = 0;
		 for (int i = 1; i < nums.size(); i++)
		 {
			 if (nums[i] < nums[i - 1]) //不要放在for的循环条件上
			 {
				 pos = i; 
				 break;
			 }
		 }

		 int low = 0, high = nums.size() - 1;
		 //判断target在那个区间
		 if (target <= nums.back())
			 low = pos;
		 else
			 high = pos - 1;
		 while (low <= high)
		 {
			 int mid = (low + high) / 2;
			 if (target > nums[mid]) low = mid + 1;
			 else if (target < nums[mid]) high = mid - 1;
			 else return mid;
		 }
		 return -1;
	 }
 };

3. 节点移动

节点移动在链表插入排序或者partition操作中非常常见,节点移动是指吧当前节点从原位置删除,然后插入到其他位置,既然涉及到节点删除,所以必须通过p->next探测要移动的节点。体会下面节点移动的模板:

ListNode* insertionSortList(ListNode* head) {
   if (!head)  return NULL;
   //因为头结点可能会改变因此使用辅助结点
    ListNode dummy(0);
    dummy.next = head;
    ListNode *p = &dummy;
    while (p->next) {
        ListNode* q = &dummy;
        while (q->next && q->next->val < p->next->val) {
            q = q->next;
        }
        //节点移动操作
        if (q->next) {
        	//1.如果要移动的节点和插入位置节点是同一个,则不操作直接向下移动
            if (p->next == q->next) {
                p = p->next;
            } 
            //2.移动节点:先删除当前节点,再把节点插入到目标位置
            else {
                ListNode* cur = p->next;
                p->next = p->next->next;
                cur->next = q->next;
                q->next = cur;
            }
        }
    }
    return dummy.next;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值