链表
链表空间不一定保证连续,为临时分配
返回值为链表节点
画图方法理清逻辑
边界条件要求严格
链表为空或链表长度为1
可以使用额外数据结构来简化调整过程,但最优解往往不需要使用额外数据结构
技巧
-
构建新的不参与运算的头节点
ListNode* dummy = new ListNode(-1)//用-1是因为node没有无参构造 dummy->next = head; ... return dummy->next;
-
画图! 举例!
简单难度
删除排序链表中的重复元素 (重复的保留一个)
leetcode 203
ListNode* deleteDuplicates(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
head->next = deleteDuplicates(head->next);
return head->val == head->next->val?head->next:head;
}
移除给定值的所有链表元素
leetcode 83
ListNode* removeElements(ListNode* head, int val) {
if (head == nullptr) return head;
head->next = removeElements(head->next, val);
return head->val == val ? head->next : head;
}
合并两个有序链表
leetcode 21
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == nullptr)
return l2;
if (l2 == nullptr)
return l1;
//递归,一步步返回两个值较小的数;
if(l1->val<l2->val){
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}
else{
l2->next = mergeTwoLists(l1,l2->next);
return l2;
}
}
反转链表
leetcode 206
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head->next == nullptr)
return head;
ListNode* next = head->next;
ListNode* newhead = reverseList(next);//遍历至最后一个节点
next->next = head;
head->next = nullptr;
return newhead;
//递归的方法其实是非常巧的,它利用递归走到链表的末端,然后再更新每一个node的next值,实现链表的反转。而newhead 的值没有发生改变,为该链表的最后一个结点,所以,反转后,我们可以得到新链表的head。
}
ListNode* reverseList2(ListNode* head) {
ListNode* prev = nullptr;
ListNode* next ;
while(head!= nullptr){
next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}
相交链表
leetcode 160
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *l1 = headA;
ListNode *l2 = headB;
while(l1!=l2){
l1 = (l1 == nullptr) ? headB: l1->next;
l2 = (l2 == nullptr) ? headA: l2->next;
}
return l1;
}
删除链表中的节点(非尾节点)
leetcode 237
将待删除节点的值赋值为下一个节点的值,然后将待删除节点的引用指向待删除节点的下下个节点。
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
环形链表1 判断是否有环
bool hasCycle(ListNode *head) {
if (head==nullptr||head->next==nullptr) return false;
ListNode* fast = head;
ListNode* slow = head;
while(fast->next !=nullptr && fast!=nullptr){
fast = fast->next->next;
slow = slow->next;
if (fast==slow) return true;
}
return false;
}
中等难度
两数相加 M
leetcode 2
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* node = new ListNode(0);
int carry = 0;
ListNode* p = l1;
ListNode* q = l2;
ListNode* cur = node;
while(p!= nullptr || q!=nullptr){
int x = (p==nullptr)?0:p->val;
int y = (q==nullptr)?0:q->val;
int sum = x+y+carry;
carry = sum/10;
ListNode* newnode = new ListNode(sum%10);
cur->next = newnode;
cur = cur->next;
if (p!= nullptr) p = p->next;
if (q!= nullptr) q = q->next;
}
if (carry>0){
cur->next = new ListNode(carry);
} return node->next;
}
删除链表的倒数第N个节点 M
leetcode 19
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* fast = head;
for(int i=0;i<n;i++){
fast = fast->next;
}
if (fast == nullptr) return head->next;//删除的是头节点
ListNode* slow = head;
while(fast->next!= nullptr){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return head;
}
两两交换链表中的节点 M
leetcode 24
给定 1->2->3->4, 你应该返回 2->1->4->3.
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换
ListNode* swapPairs(ListNode* head) {
ListNode* node = new ListNode(-1);//新建一个空节点,永远连着头节点
node->next = head;
ListNode* pre = node;
while (pre->next!= nullptr && pre->next->next != nullptr){
ListNode* cur = pre->next;
ListNode* next = pre->next->next;
cur->next = next->next;
next->next = cur;
pre->next = next;
pre = cur; // 1-2-3-4 变成 2-1-3-4 但cur仍是2,相当于每次前进两格了
}
return node->next;
}
反转链表II M
leetcode 92
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
1->2->3->4->5->6->NULL node1 = 2 node2 = 3
1->3->2->4->5->6->NULL node1 = 2 node2 = 4
1->4->3->2->5->6->NULL node1 = 2 node2 = 5
1->5->4->3->2->6->NULL node1 = 2 node2 = 6
1->6->5->4->3->2->NULL
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode *dummy = new ListNode(-1);
dummy->next = head;//dummy->next获取新链表头结点
if(head== nullptr ||head->next == nullptr)
return dummy->next;
ListNode *start = dummy;
ListNode *node1,*node2;
for(int i=0;i<m-1;i++){
start = start->next;//从dummy开始遍历m次,获得m-1节点
}
node1 = start->next; //指针1,它的值永远是第m个节点的值
node2 = node1->next; //指针2
for(int i=0;i<n-m;i++){//遍历(n-m)次
node1->next = node2->next;
// node2后面的元素接在node1后面
node2->next = start->next;
// start后面的元素放在node2后面
start->next = node2;
// node2放在start后面,反转
// 其实就是调换start->next和node2,node1值不变,位置一直前进
node2 = node1->next;
// node2前进
}
return dummy->next;
}
删除排序链表中的重复元素 II (重复的都删掉)
leetcode 82 + 剑指offer 18
ListNode* deleteDuplication(ListNode* pHead)
{
ListNode* dummy = new ListNode(-1);
dummy->next = pHead;
ListNode* cur = pHead;
ListNode* last = dummy; // 最后一个保存的节点
while(cur!=nullptr&&cur->next!=nullptr)
{
if(cur->val==cur->next->val){
int val = cur->val;
while(cur!=nullptr&&cur->val == val){//遍历掉相同的节点
cur = cur->next;
}
last->next = cur;
}
else
{
last = cur;
cur = cur->next;
}
}
return dummy->next;
}
环形链表2 返回入环节点(HashTable)
剑指offer
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
// hash table
if (pHead == nullptr) return nullptr;
set<ListNode*> s;
while(pHead!=nullptr)
{
if (s.find(pHead)!=s.end()) return pHead;
else s.insert(pHead);
pHead = pHead->next;
}
return nullptr;
}
难题
复杂链表的复制
- 直接复制 O(n2)
- 用空间换时间,哈希表 O(n)
-
- 复制每个节点,如:复制节点A得到A1,将A1插入节点A后面
- 遍历链表,A1->random = A->random->next;
- 将链表拆分成原链表和复制后的链表
if (pHead == nullptr) return nullptr;
RandomListNode* cur = pHead;
while(cur!=nullptr){
RandomListNode* n = new RandomListNode(cur->label);//初始化新建节点
n->next = cur->next;
cur->next = n;
cur = n->next;
}//在原节点后面复制节点
cur = pHead;
while(cur!=nullptr){
RandomListNode* node = cur->next;
if (cur->random!=nullptr){
node->random = cur->random->next;
}
cur = node->next;
// 复制random节点
}
RandomListNode* newhead = pHead->next;
RandomListNode* tmp = newhead;
cur = pHead;
while(cur!=nullptr){
cur->next = cur->next->next;
if(tmp->next!=nullptr) tmp->next = tmp->next->next;
cur = cur->next;
tmp = tmp->next;
}
return newhead;
}
排序链表(O(nlogn))
合并k个排序链表
环形链表II
分隔链表
二叉搜索树与双向链表
数组、排序与查找
基本排序算法
冒泡排序
每一轮迭代交换一次元素
def bubble_sort(seq):
n = len(seq)
for i in range(n-1):
for j in range(n-1-i):
if seq[j] > seq[j+1]:
seq[j],seq[j+1] = seq[j+1],seq[j]
return seq
选择排序
def select_sort(seq):
n = len(seq)
for i in range(n-1):
min_idx = i
for j in range(i+1,n):
if seq[j]<seq[min_idx]:
min_idx = j
if min_idx ! = i:
seq[i],seq[min_idx] = seq[min_idx],seq[i]
return seq
插入排序
def insert_sort(seq):
n = len(seq)
for i in range(1,n):
pos = i
value = seq[i]
while pos>0 and value < seq[pos-1]:
seq[pos] = seq[pos-1]
pos-=1
seq[pos] = value
return seq
高级排序算法
归并排序
O(nlg(n))
def merge_sort(seq):
if len(seq)<= 1:
return seq
else:
mid = int(len(seq)/2)
left_half = merge_sort(seq[:mid])
right_half = merge_sort(seq[mid:])
new_seq = merge_sorted_list(left_half,right_half)
return new_seq
def merge_sorted_list(left,right): #合并有序数组
len_a = len(left)
len_b = len(right)
a = b = 0
new = []
while a < len_a and b < len_b:
if left[a]<right[b]:
new.append(left[a])
a += 1
else:
new.append(right[b])
b += 1
new += right[b:]
new += left[a:]
return new
快速排序
def quicksort(seq,left,right):
if left<right:
pivot = partition(seq,left,right)
quicksort(seq,left,pivot)
quicksort(seq,pivot+1,right)
return seq
def partition(seq,left,right):
array = left-1
pivot_index = right
pivot = seq[right]
for j in range(left,right):# 只遍历一遍
if seq[j]<=pivot: # 比pivot小则array继续前进,同时交换元素
array += 1
seq[j],seq[array] = seq[array],seq[j]
seq[array+1],seq[pivot_index] = pivot,seq[array+1] # 把pivot放到array+1的位置
return array
void swap(int &fir,int &sec)
{
int temp = fir;
fir = sec;
sec = temp;
}
int partition(vector<int> &input, int left, int right){//&input 很重要!!
if(input.empty() || left>right) return -1; //+
int array = left-1;
int pivot = input[right];
for(int i=left;i<right;i++){
if (input[i]<=pivot){
array++;
swap(input[array],input[i]);// not necessarily
}
}
swap(input[array+1],input[right]);
return array+1;
}
void quicksort(vector<int>& input, int left, int right){
if (left<right){
int p = partition(input,left,right);
quicksort(input,p,right);
quicksort(input,left,p-1)
}
}
堆排序
def heap_sort(seq):
n = len(seq)
for i in range(n//2-1,-1,-1):
sift(seq,i,n-1)
for i in range(n-1,-1,-1):
seq[0],seq[i]= seq[i],seq[0]
sift(seq,0,i-1)
print(seq)
return seq
def sift(seq,left,right):
i = left
j = 2*i+1
tmp = seq[i]
while j<= right:
if j<right and seq[j]<seq[j+1]:
j +=1
if tmp <seq[j]:
seq[i] = seq[j]
i = j
j = 2*i+1
else:
break
seq[i] = tmp
def heapSort2(alist):
if alist == None or len(alist) == 0:
return
length = len(alist)
output = []
for i in range(length):
tempLen = len(alist)
for j in range(tempLen//2-1, -1, -1):
preIndex = j
preVal, heap = alist[preIndex], False
while 2 * preIndex < tempLen - 1 and not heap:
curIndex = 2 * preIndex + 1
if curIndex < tempLen - 1:
if alist[curIndex] < alist[curIndex+1]:
curIndex += 1
if preVal >= alist[curIndex]:
heap = True
else:
alist[preIndex] = alist[curIndex]
preIndex = curIndex
alist[preIndex] = preVal
output.insert(0, alist.pop(0))
return output
希尔排序
直接插入排序的升级
def shell_sort(seq):
gap = len(seq)//2
while gap >0:
print(gap)
for i in range(gap):
for j in range(i+gap,len(seq),gap):
value = seq[j]
pos = j
while pos>=gap and seq[pos-gap]>value:
seq[pos] = seq[pos-gap]
pos -= gap
seq[pos] =value
gap = gap//2
print(seq)
return seq
二分查找
注意溢出 mid = (left+right)/2 加法可能溢出 -> mid = left+(right-left)/2
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int length = array.length;
if(length == 0){
return 0;
}
int firstK = getFirstK(array, k, 0, length-1);
int lastK = getLastK(array, k, 0, length-1);
if(firstK != -1 && lastK != -1){
return lastK - firstK + 1;
}
return 0;
}
//递归写法
private int getFirstK(int [] array , int k, int start, int end){
if(start > end){
return -1;
}
int mid = (start + end) >> 1; //使用>>1 防止溢出
if(array[mid] > k){
return getFirstK(array, k, start, mid-1);
}else if (array[mid] < k){
return getFirstK(array, k, mid+1, end);
}else if(mid-1 >=0 && array[mid-1] == k){
return getFirstK(array, k, start, mid-1);
}else{
return mid;
}
}
//循环写法
private int getLastK(int [] array , int k, int start, int end){
int length = array.length;
int mid = (start + end) >> 1;
while(start <= end){
if(array[mid] > k){
end = mid-1;
}else if(array[mid] < k){
start = mid+1;
}else if(mid+1 < length && array[mid+1] == k){
start = mid+1;
}else{
return mid;
}
mid = (start + end) >> 1;
}
return -1;
}
}
旋转排序数组系列
-
旋转排序数组的最小值
int minNumberInRotateArray(vector<int> rotateArray) { int l = 0, r = rotateArray.size()-1,mid = l; //mid 初始化为l,考虑没有旋转的情况 if (r==0) return 0; while(rotateArray[l]>=rotateArray[r]){ if (r-l == 1){ mid = r; break; } mid = (l+r)/2; if (rotateArray[l]<=rotateArray[mid]) l = mid; else if (rotateArray[mid]<=rotateArray[r]) r = mid; } return rotateArray[mid]; }
-
搜索旋转排序数组
bool search(vector<int>& nums, int target) { int l =0, r= nums.size()-1; if (r==-1) return false; if (r==0) return (nums[0]==target); while(l<r){ int mid = (l+r)>>1; if (nums[mid]==target || nums[l] == target || nums[r] == target) return true; if (r-l == 1) break; if (nums[mid]<nums[r]) { if (nums[mid]<target&&target<nums[r]) l = mid+1; else r = mid; } else if (nums[mid]>nums[l]) { if (nums[l]<target && target<nums[mid]) r = mid// C++不要用a<t<b a==b==c !!! else l = mid+1; } } return false; }
-
搜索旋转排序数组(有重复值)
nums[r] = nums[m]=nums[l] 从r到l遍历
... if (nums[mid] == nums[l] && nums[l] == nums[r]){// for(int j = l;j<=r;j++){ if (nums[j] == target) return true; } return false; ...
局部最小值(寻找峰值)
int findPeakElement(vector<int>& nums) {
int l = 0, r = nums.size()-1,mid = 0;
if(r==-1 or r ==0) return 0;
else if (nums[r]>nums[r-1]) return r;
else if (nums[0]>nums[1]) return 0;
else
{
while(l<r)
{
mid = l+(r-l)/2;
if (nums[mid]>nums[mid-1] and nums[mid]>nums[mid+1]) return mid;
else if (nums[mid]>nums[mid-1]) l = mid+1;
else if (nums[mid]>nums[mid+1]) r = mid;
else l = mid+1;//r = mid
}
return 0;
}
k的n次方
double myPow(double x, int n) {
double ans = 1.0;
for(int i =n;i!=0;i=i/2){
if(i%2!=0){
ans = ans*x;
}
x = x*x;
}
return x<0?1/ans:ans;
}
// i = 10 x = 2*2 i = 5 x = 2**4 ans = 2**2 i = 2 x = 2**8 ans = 2**2 i = 1 x = 2*16 ans = 2**(2+8)
//2 10 5 2 2 1 2 9 4