牛客TOP101刷题记录
作者:互联网
一、排序总结
排序算法大致可以分为两类:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序;如果需要使用外存,则称为外排序。
内排序有以下几类:
- 插入排序:直接插入、二分法插入、希尔
- 选择排序:直接选择、堆排序
- 交换排序:冒泡、快速
- 归并排序
- 基数排序
排序方法 |
时间复杂度(平均) |
时间复杂度(最坏) |
时间复杂度(最好) |
空间复杂度 |
稳定性 |
复杂性 |
直接插入排序 |
O(n2) |
O(n2) |
O(n) |
O(1) |
稳定 |
简单 |
希尔排序 |
O(nlog2n) |
O(n2) |
O(n1.3) |
O(1) |
不稳定 |
较复杂 |
直接选择排序 |
O(n2) |
O(n2) |
O(n2) |
O(1) |
不稳定 |
简单 |
堆排序 |
O(nlog2n) |
O(nlog2n) |
O(nlog2n) |
O(1) |
不稳定 |
较复杂 |
冒泡排序 |
O(n2) |
O(n2) |
O(n) |
O(1) |
稳定 |
简单 |
快速排序 |
O(nlog2n) |
O(n2) |
O(nlog2n) |
O(nlog2n) |
不稳定 |
较复杂 |
归并排序 |
O(nlog2n) |
O(nlog2n) |
O(nlog2n) |
O(n) |
稳定 |
较复杂 |
基数排序 |
O(d(n+r)) |
O(d(n+r)) |
O(d(n+r)) |
O(n+r) |
稳定 |
较复杂 |
1.插入排序
思想:每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的合适位置,直到全部插入排序完为止。
关键点:在前面已经排好序的序列中找到合适的位置插入。
二分查找排序,是在普通插入排序的基础上,引入了二分查找的思想。
希尔排序是对直接插入排序的改良,关键点在于,让较大或者较小的数,快速靠近自身所属的位置。
2.选择排序
思想:每趟从待排序的记录序列中选择关键字最小的记录放置到已排序序列的最后位置,直到全部排完
关键点:找最小的记录
堆排序:通过堆,这种可以快速找到最小的记录的方式。
3.交换(冒泡)排序
思想:利用交换元素的位置进行排序,每次两两比较待排序的元素,直到全部排完
关键问题:理清楚需要进行几轮排序
快速排序:通过找分界点,将较大的元素和较小的元素快速区分开,直至各区间只有一个数
4.归并排序
思想:使用了分治法。分:将待排序的序列进行“对半”分,直至各区间只有一个数;治:将有序序列进行合并。
关键问题:分与治
5.基数排序
思想:将待比较的正整数统一为同样的数位长度,短的补零。然后从最低位开始,依次进行一次排序;从最低位到最高位。
关键问题:进制和数位
计数排序:适用于范围比较小的整数序列。找到最小值和最大值,然后以此建立数组,统计出现个数,然后排序。
桶排序:遍历数组,找到最小值和最大值,然后依次划分为多个区间(桶),在桶里单独排序。
二、TOP101
题目来源:热门100题
1.链表
1.反转链表
两种反转方法:
关键点:三个指针,pre, cur,next
class Solution { public: ListNode* ReverseList(ListNode* pHead) {
//关键点:三个指针,pre, cur,next ListNode *pre = nullptr; ListNode *cur = pHead; ListNode *nex = nullptr; // 这里可以指向nullptr,循环里面要重新指向 while (cur) { nex = cur->next; cur->next = pre; pre = cur; cur = nex; } return pre; } };
class Solution { public: ListNode* ReverseList(ListNode* pHead) { ListNode* dummy = new ListNode(0); dummy->next = pHead; ListNode* pre = dummy, *cur = pHead; while(cur->next){ ListNode* temp = cur->next; cur->next = temp->next; temp->next = pre->next; pre->next = temp; } return dummy->next; } };
关键点:添加虚拟头结点,在头上反转
2.指定区间反转
关键点:虚拟头节点简化操作,使用第二种反转方法
ListNode* dummy = new ListNode(0)
3.每k个一组反转
关键点:特殊情况处理(k == 1, head == nullptr, cur == nullptr)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * struct ListNode { 3 * int val; 4 * struct ListNode *next; 5 * }; 6 */ 7 8 class Solution { 9 public: 10 /** 11 * 12 * @param head ListNode类 13 * @param k int整型 14 * @return ListNode类 15 */ 16 void reverse(ListNode* list){ 17 ListNode* pre = nullptr, *cur = list, *next; 18 while(cur){ 19 next = cur->next; 20 cur->next = pre; 21 pre = cur; 22 cur = next; 23 } 24 } 25 26 ListNode* reverseKGroup(ListNode* head, int k) { 27 if(!head || k == 1) return head; 28 // write code here 29 ListNode* dummy = new ListNode(0); 30 dummy->next = head; 31 32 ListNode* pre = dummy, *cur = head, *next = cur, *next_head; 33 while(next->next){ 34 for(int i = 1; i < k; i++){ 35 if(next->next){ 36 next = next->next; 37 }else{ 38 return dummy->next; 39 } 40 } 41 next_head = next->next; 42 pre->next = nullptr; 43 next->next = nullptr; 44 reverse(cur); 45 //connect 46 pre->next = next; 47 cur->next = next_head; 48 //change 49 pre = cur; 50 cur = next_head; 51 next = cur; 52 if(cur == nullptr) break; 53 } 54 return dummy->next; 55 } 56 };View Code
4.合并两个链表
关键点:两个指针指向两个链表的未排序节点
5.合并k个已排序的链表
三种解法:分治;顺序合并;优先队列
这道题目可以作为分治法的练习题。
6.判断链表中是否有环
关键点:快慢指针,两指针相遇则说明有环。
7.链表中环结点的入口结点
关键点:第一次相遇说明有环,快指针返回,再次相遇则是环入口
8.链表中倒数最后k个结点
关键点:双指针
9.删除链表的倒数第n个结点
关键点:创建虚拟头结点来避免分类讨论,找到需要删除的结点的前一个结点
10.两个链表的第一个公共结点
关键点:确认两个链表的长度,然后将链表对齐
11.链表相加
关键点:先反转链表,再相加,直至两链表均为空,且无进位。
12.单链表的排序
关键点:使用分治法;用快慢指针将链表进行 “ 分 ”,合并有序链表为 “ 治 ”
出错点:head 和 !head的具体含义
13.判断一个链表是否为回文结构
关键点:反转(折叠),然后对比
14.链表的奇偶重排
关键点:终止条件为指向偶数项及其后一项的指针不为空
15.删除有序链表中重复的元素1
关键点:需要熟练,
16.删除有序链表中重复的元素2
关键点:找到要删除的元素的前一个元素。
2.二分查找/排序
17.二分查找
关键点:记录左右边界,查找中点,然后压缩范围。l <= r, 注意等于情况
18.二维数组中的查找
关键点:利用性质,从左下角或者右上角出发寻找
错误点:for循环的判断条件要用逻辑运算符链接,不能用逗号
19.寻找峰值
关键点:二分查找,压缩范围。
20.数组中的逆序对
关键点:归并排序;在最外层开辟一个足够大的数组,然后传引到函数
标签:ListNode,cur,关键点,next,链表,牛客,排序,TOP101,刷题 来源: https://www.cnblogs.com/yuan-sen/p/16107117.html