文章目录
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;
}
};
- 二分法查找
//选择这一种写法
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;
}