归并排序
排序一段序列分为排序前部分、后部分,再合并两个已经排序好的部分。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* merge(ListNode* head1,ListNode* head2)
//将两个有序链表合并成为一个有序链表,返回链表的头指针
{
ListNode* dummyhead = new ListNode(-1);
ListNode* currents = dummyhead;
while(head1 != NULL&&head2 != NULL)
{
if(head1->val <= head2->val)
//每次取出head1节点与head2节点中的较小值
//将其放插入合并的链表之中
{
currents->next = new ListNode(head1->val);
head1 = head1->next;
currents = currents->next;
}
else
{
currents->next = new ListNode(head2->val);
head2 = head2->next;
currents = currents->next;
}
}
while(head1 != NULL)
//如果其中还有节点的数值未被放完的时候
//将剩余的节点插入到合并的链表之中
{
currents->next = new ListNode(head1->val);
currents = currents->next;
head1 = head1->next;
}
while(head2 != NULL)
{
currents->next = new ListNode(head2->val);
currents = currents->next;
head2 = head2->next;
}
currents->next = NULL;
return dummyhead->next;
//返回合并链表的头指针
}
ListNode* cuts(ListNode* head,int steps)
{
//切除链表,返回下一个链表的头指针
ListNode* dummyhead = new ListNode(-1);
dummyhead->next = head;
ListNode* currents = dummyhead;
for(int i=0;i<steps;i++)
{
if(currents->next == NULL)
//放置链表的末尾有长度不够step的部分
{
break;
}
currents = currents->next;
}
ListNode* u = currents->next;
//提前保存下一个链表的头指针
currents->next = NULL;
//将链表切断
return u;
//返回下一个链表的头指针
}
ListNode* sortList(ListNode* head) {
ListNode* dummyhead = new ListNode(-1);
dummyhead->next = head; //dummy指针确保头结点准确
ListNode* currents = head;
int total = 0;
//确定链表长度
while(currents)
{
total++;
currents = currents->next;
}
ListNode* lefts;
ListNode* rights;
ListNode* nextleft;
lefts = dummyhead->next;
ListNode* tail = dummyhead;
for(int step = 1;step < total;step=step*2)
{
tail = dummyhead;
//tail保存合并链表的头指针
lefts = dummyhead->next;
//两两合并,lefts指向第一个链表
while(lefts != NULL)
{
rights = cuts(lefts,step);
//切割后返回下一个链表的头指针
nextleft = cuts(rights,step);
tail->next = merge(lefts,rights);
//将lefts链表与rights链表合并
while(tail->next != NULL)
{
tail = tail->next;
}
//尾指针后移,变为下一个节点的头指针
lefts = nextleft;
//lefts变为下一个合并链表的头指针
}
}
return dummyhead->next;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Soulution{
public:
ListNode* sortList(ListNode *head)
{
if(head == NULL || head->next ==NULL)
return head;
ListNode *end = head;
while(end && end->next){
end = end->next;
}
return mergeSort(head,end);
}
//归并排序
ListNode* mergeSort(Listnode *start, Listnode *end){
//一个节点直接返回
if(start == end) return start;
//两个节点则返回小到大
if(start->next == end)
{
if(start->val >= end->val)
{
int temp = start->val;
start->val = end->val;
end->val = temp;
}
return start;
}
//快慢指针确定中点,二分法
Listnode *slow = start,*fast = start;
while(fast!=end){
slow = slow->next;
fast = fast->next;
if(fast->next) fast = fast->next;
}
//第一步,递归,排序右边
ListNode * rightList = mergeSort(slow->next, end);
slow->next = NULL; //将两个链表分开
//第二步,排序左边
ListNode * leftList = mergeSort(start, slow);
//第三步,合并
ListNode *pHead = NULL, *pEnd = NULL;//合并链表的头、尾
if (rightList == NULL) {
return leftList;
}
//初始化头结点、尾节点
if (rightList->val > leftList->val) {
pEnd = pHead = leftList;
leftList = leftList->next;
}
else {
pEnd = pHead = rightList;
rightList = rightList->next;
}
//合并,每次将较小值放入新链表
while(rightList && leftList)
{
if(rightList->val > leftList->val){
pEnd ->next = leftList;
pEnd = pEnd->next;
leftList = leftList->next;
}
else{
pEnd ->next = rightList;
pEnd = pEnd->next;
rightList = rightList->next;
}
}
//添加剩余链表
if(rightList->next==NULL) pEnd->next = leftList;
else pEnd->next = rightList;
return pHead;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL) {
return head;
}
return mergeSort(head);//去掉链表尾端的寻找
}
ListNode* mergeSort(ListNode* head) {
if (head == NULL || head ->next == NULL) {//这段链表只有一个节点
return head;
}
//快慢指针,定位链表中间
ListNode *slowPtr = head, *fastPtr = head->next;
while (fastPtr != NULL && fastPtr->next != NULL) {
slowPtr = slowPtr->next;//慢指针走一步
fastPtr = fastPtr->next;//快指针走两步
if (fastPtr != NULL && fastPtr->next != NULL) {
fastPtr = fastPtr->next;//快指针走两步
}
}
//第一步 递归,排序右半
ListNode * rightList = mergeSort(slowPtr->next);
slowPtr->next = NULL;//将左右两部分切开
//第二步 递归,排序左半
ListNode * leftList = mergeSort(head);
//第三步 合并
ListNode *pHead = NULL, *pEnd = NULL;//合并链表的头、尾
if (rightList == NULL) {
return leftList;
}
//初始化头结点、尾节点
if (rightList->val > leftList->val) {
pEnd = pHead = leftList;
leftList = leftList->next;
}
else {
pEnd = pHead = rightList;
rightList = rightList->next;
}
//合并,每次将较小值放入新链表
while (rightList && leftList) {
if (rightList->val > leftList->val) {
pEnd->next = leftList;
pEnd = pEnd->next;
leftList = leftList->next;
}
else {
pEnd->next = rightList;
pEnd = pEnd->next;
rightList = rightList->next;
}
}
//可能某个链表有剩余
if (rightList == NULL) {
pEnd->next = leftList;
}
else {
pEnd->next = rightList;
}
return pHead;
}
};
快速排序
归并排序把数组分成两个数组分别排序再合并,而快速排序的方式是两个子数组都有序时整个数组也就有序了。
归并排序按数量将数组分成两半,快速排序按大小将数组分成两半。
时间复杂度 : 平均情况 O(N),最坏情况 O(N^2)
空间复杂度 : O(1)
class Solution
{
public:
int findKthLargest(vector<int> &nums, int k)
{
int result = 0;
int numsSize = int(nums.size());
if (numsSize == 0 || k > numsSize)
{
return 0;
}
//寻找第kMIN小的数
int kMin = numsSize - k + 1;
result = select(nums, 0, numsSize - 1, kMin);
return result;
}
int select(vector<int> &nums, int left, int right, int target)
{
if (left == right)
{
return nums[left];
}
int cut = partition(nums, left, right);
//当前第currentResult小的元素
int currentResult = cut - left + 1;
if (target == currentResult)
{
return nums[cut];
}
else if (target < currentResult)
{
return select(nums, left, cut - 1, target);
}
else
{
//寻找接下来第target - currentResult小的数
return select(nums, cut + 1, right, target - currentResult);
}
return 0;
}
int partition(vector<int> &nums, int left, int right)
{
int cut = nums[right];
//i指向大堆的最左边的数,j指向下一个判断的数
int i = left;
for (int j = left; j < right; j++)
{
if (nums[j] <= cut)
{
exchange(nums[i], nums[j]);
i++;
}
}
exchange(nums[i], nums[right]);
return i;
}
void exchange(int &a, int &b)
{
int tmpInt = a;
a = b;
b = tmpInt;
return;
}
};
class Solution
{
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
if(input.empty() || k<=0 || k > input.size()) return vector<int>(); //处理异常输入
int left = 0, right = input.size()-1;
int pivot_pos;
while(left <= right)//类似二分查找法
{
pivot_pos = partition(input, left, right);//如果要求最大的第k个数,可以对partition函数进行改造
if(pivot_pos < k-1)
left = pivot_pos + 1;
else if(pivot_pos > k-1)
right = pivot_pos - 1;
else
break;//此题要求的是返回最小的前k个数,如果仅返回最小的第k个数,直接在这里return a[pivot_pos]即可
}
vector<int> result(input.begin(), input.begin()+k); //构造结果向量
return result;
}
private:
int partition(vector<int>& a, int left, int right)
{
//随机初始化枢轴 5ms
//srand(time(nullptr)); //以当前时间为随机生成器的种子
//int pivotpos = rand()%(right - left + 1) + left; //产生【left,right】之间的数
//swap(a[pivotpos], a[left]); //将枢轴暂时放入起始位置
int pivot = left; //枢轴位置 4ms
while(left<right)
{
while(left < right && a[right] >= a[pivot]) right--; //找到本次扫描中第一个不满足枢轴规律的高位数
while(left < right && a[left] <= a[pivot]) left++; //找到本次扫描中第一个不满足枢轴规律的低位数
swap(a[left], a[right]); //交换以使满足枢轴规律
}//最后结果是left和right均指向枢轴位置
swap(a[left], a[pivot]); //将枢轴移动到位
return left; //返回枢轴位置
}
};
堆排序
利用小顶堆的特性(堆顶元素最小),先对前K个数组元素进行"原地"建小顶堆,建完小顶堆后,堆顶的元素最小,正好是这K个元素的第K大元素。
然后遍历剩下的元素 nums[k] ~ nums[len-1]
1、如果比堆顶元素小,跳过
2、如果比堆顶元素大,和堆顶元素交换后重新堆化
建堆 buildHeap 时间复杂度 O(K),遍历剩下元素并且堆化 时间复杂度(N-K)*O(logK),总体的时间复杂度 O(NlogK)
int findKthLargest(vector<int>& nums, int k)
{
priority_queue<int,vector<int>,greater<int>> pq; //维护一个 k 大小的小顶堆,堆顶就是第 k 个最大的数
for(auto &n : nums){
if(pq.size()>=k && pq.top()>=n) continue;
pq.push(n);
if(pq.size()>k) pq.pop();
}
return pq.top();
}
/*
2
*/
class Solution
{
public:
int findKthLargest(vector<int> &nums, int k)
{
int numsSize = int(nums.size());
if (numsSize == 0 || k > numsSize) return 0;
priority_queue<int, vector<int>, greater<int>> store;
//堆中维持k个最大数
for (int i = 0; i < numsSize; i++)
{
store.push(nums[i]);
while (int(store.size()) > k) store.pop();
}
return store.top();
}
};
//O(nlogn + klogn),O(1)
class Solution
{
public:
int findKthLargest(vector<int>& nums, int k)
{
make_heap(nums.begin(),nums.end());//构建大顶堆,用nums存储,与下面区别就是节省了空间
for(int i = 0; i<k-1;i++)
{
pop_heap(nums.begin(),nums.end()); //将堆顶元素移至末尾,重新调整使(begin~end-1)的元素满足堆规律
nums.pop_back(); //移除末尾元素
}
return nums[0];
}
};