排序算法
- 快排是不稳定的(可以加关键字,即二元组进行排序)
快速排序算法(分治思想)
步骤
- 确定分界点:可以用最小下标值、中间值以及随机选取
- 调整区间:双指针
- 递归处理左右两段
代码模板
几种模板详解转载快速排序
注意y总的do-while模板边界。如果选择i-1,则分界点不能选择q[l];若选择j,则分界点不能选择q[r],否则可能会死循环。.
void quick_sort(int q[], int l, int r)
{
if (l >= r) return;
int x = q[l], i = l, j = r; // 这里必须要用q[l]
while (i <= j)
{
while (i <= j && q[i] <= x) i ++ ;
while (i <= j && q[j] >= x) j -- ;
if (i < j) swap(q[i], q[j]);
}
swap(q[l], q[j]); // 记得把pivot换到对应的位置
// 一次划分完后区间被分为[l, j - 1][j][j + 1, r], j就是选的pivot
quick_sort(q, l, j - 1);
quick_sort(q, j + 1, r);
}
单链表的快速排序(只改变节点的val值)
//单链表的快速排序:
class Solution {
public:
ListNode* sortList(ListNode* head) {
quickSort(head, nullptr);
return head;
}
void quickSort(ListNode* head, ListNode* tail) {
if (head == tail || head->next == nullptr) {
return;
}
ListNode* mid = partition(head, tail);
quickSort(head, mid);
quickSort(mid->next, tail);
}
ListNode* partition(ListNode* head, ListNode * tail) {
int pivot = head->val;
ListNode* s = head;
ListNode* cur = head->next;
while (cur != nullptr && cur != tail)
{
if (cur->val < pivot) {
s = s->next;
swap(s, cur);
}
cur = cur->next;
}
swap(s, head);
return s;
}
void swap(ListNode* a, ListNode* b) {
int temp = a->val;
a->val = b->val;
b->val = temp;
}
};
单链表快速排序(改变链表的节点位置)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* get_tail(ListNode* head)
{
while(head->next) head=head->next;
return head;
}
ListNode* quickSortList(ListNode* head) {
if(!head||!head->next) return head;
auto left=new ListNode(-1),mid=new ListNode(-1),right=new ListNode(-1);
auto ltail=left,mtail=mid,rtail=right;
int val=head->val;
for(auto p=head;p;p=p->next)
{
if(p->val<val) ltail=ltail->next=p;
else if(p->val==val) mtail=mtail->next=p;
else rtail=rtail->next=p;
}
ltail->next=mtail->next=rtail->next=NULL;
left->next=quickSortList(left->next);
right->next=quickSortList(right->next);
get_tail(left)->next=mid->next;
get_tail(left)->next=right->next;
return left->next;
}
};
时间复杂度分析
- 快排的平均时间复杂度是O(nlogn).
归并排序
- 归并排序是稳定的
步骤
- 确定分界点:数组的中心点
- 递归排序left,right
- 归并:左边右边合二为一(双指针算法)
代码模板
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], tmp[N];
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid), merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
merge_sort(a, 0, n - 1);
for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);
return 0;
}
归并的迭代方法
void mergesort(int k[],int n)
{
int i, left_min,left_max,right_min,right_max;
int *temp=(int *)malloc(n*sizeof(int));
for(i=1;i<n;i*=2)
{
int next=0;
for(left_min=0;left_min<n-i;left_min=right_max)
{
right_min=left_max=left_min+i;
right_max=left_max+i;
if(right_max>n)
{
right_max=n;
}
while(left_min<left_max&&right_min<right_max)
{
if(k[left_min]<k[right_min])
{
temp[next++]=k[left_min++];
}
else
{
temp[next++]=k[right_min++];
}
}
while(left_min<left_max) temp[next++]=k[left_min++];
while(right_min<right_max) temp[next++]=k[right_min++];
}
for(int m=0;m<n;m++) k[m]=temp[m];
}
}
归并实现求数列逆序对的个数
#include<iostream>
using namespace std;
int n;
const int N=100010;
int a[N];
long long res;
void mergesort(int k[],int i,int j)
{
if(i>=j) return;
int mid=i+j>>1;
mergesort(k,i,mid);
mergesort(k,mid+1,j);
int temp[N],l=i,r=mid+1,u=0;
while(l<=mid&&r<=j)
{
if(k[l]<=k[r]) temp[u++]=k[l++];
else {
temp[u++]=k[r++];
res+=mid-l+1;
}
}
while(l<=mid) temp[u++]=k[l++];
while(r<=j) temp[u++]=k[r++];
u=0;
for(int q=i;q<=j;q++) k[q]=temp[u++];
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
mergesort(a,0,n-1);
cout<<res<<endl;
//for(int q=0;q<n;q++) cout<<a[q]<<endl;
return 0;
}
时间复杂度分析
- 归并排序的时间复杂度一直是O(nlogn)
冒泡排序
基本思想
- 两两相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
代码实现
//可以设置一个flag变量来进行剪枝操作;
void BubbleSort(int k[],int n)
{
int i,j,temp;
for(i=0;i<n-1;i++)
for(j=n-1;j>i;j--)
{
if(k[j-1]>k[j])
{
temp=k[j-1];
k[j-1]=k[j];
k[j]=temp;
}
}
}
时间复杂度分析
- 冒泡时间复杂度是O(n^2)
选择排序
基本思想
- 通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换。
代码实现
void SelectSort(int k[],int n)
{
int i,j,temp;
for(i=0;i<n-1;i++)
{
int min=i;
for(j=i+1;j<n;j++)
{
if(k[j]<k[min])
{
min=j;
}
}
if(min!=i)
{
temp=k[i];
k[i]=k[min];
k[min]=temp;
}
}
}
单链表选择排序代码实现
#include <stdio.h>
#include <stdlib.h>
struct node{
int data;
struct node *next;
};
void printList(struct node *head){
struct node *t=head;
while(t){
printf("%d ",t->data);
t=t->next;
}
}
struct node * selectSort(struct node *head)
/*选择排序
在原链表中一轮轮地找最大的那个结点,找到之后就把它从原链表中抽出来用头插法加到新的链表中。
需要注意这个最大的结点是否为头结点
*/
{
struct node *head1,*max,*premax,*t,*pret;
//head1:新链表的头指针;max:原链表中最大的结点;premax:最大结点的前驱结点;t:用于遍历原链表寻找最大结点;pret:t的前驱结点
head1=NULL;
while(head)//一遍遍地找原链表中当前最大结点
{
max=head;
premax=NULL;
pret=head;
t=head->next;
//1、寻找最大结点
while(t){
if(t->data>max->data){
max=t;
premax=pret;
}
pret=t;
t=t->next;
}
//2、让这个结点离开原链表
if(max==head)//如果找到的结点是第一个结点
head=head->next;//让头指针向后移一位就行
else
premax->next=max->next;
//3、把此结点头插法插入新链表
max->next=head1;
head1=max;
}
return head1;
}
int main()
{
struct node *head,*p,*q;
int i,n,a;
scanf("%d",&n);
head=NULL;
for(i=0;i<n;i++){
p=(struct node *)malloc(sizeof(struct node));
scanf("%d",&a);
p->data=a;
p->next=NULL;
if(!head){
head=p;
}else{
q->next=p;
}
q=p;
}
printList(selectSort(head));
}
时间复杂度分析
- 选择排序时间复杂度是O(n^2)
直接插入排序
基本思想
- 将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。
代码实现
void InsertSort(int k[],int n)
{
int i,j,temp;
for(i=1;i<n;i++)
{
if(k[i]<k[i-1])
{
temp=k[i];
for(j=i-1;k[j]>temp&&j>=0;j--)
{
k[j+1]=k[j];
}
k[j+1]=temp;
}
}
}
时间复杂度分析
- 直接插入排序时间复杂度是O(n^2)
希尔排序(插入排序的一种)
基本思路
代码实现
void InsertSort(int k[],int n)
{
int i,j,temp;
int gap=n;
do
{
gap=gap/3+1;
for(i=gap;i<n;i++)
{
if(k[i]<k[i-gap])
{
temp=k[i];
for(j=i-gap;k[j]>temp&&j>=0;j-=gap)
{
k[j+gap]=k[j];
}
k[j+gap]=temp;
}
}
}while(gap>1);
}
时间复杂度分析
- 希尔排序的时间复杂度是:O(nlogn)~O(n2),平均时间复杂度大致是O(n√n)
堆排序(完全二叉树)
堆排序的步骤
- 构造一个大根堆,取堆顶数字。
- 将剩下的数字构建一个大根堆,取堆顶数字。
- 重复以上操作,直到取完堆中的数字,最终得到一个从大到小的序列。
void swap(int k[],int i,int j)
{
int temp;
temp=k[i];
k[i]=k[j];
k[j]=temp;
}
void HeapAdjust(int k[],int s,int n)
{
int i;
int temp=k[s];
for(i=2*s;i<=n;i*=2)
{
if(i<n&&k[i]<k[i+1])
{
i++;
}
if(temp>=k[i])
{
break;
}
k[s]=k[i];
s=i;
//swap(k,s,i);
}
k[s]=temp;
}
void HeapSort(int k[],int n)
{
int i;
for(int i=n/2;i>0;i--)
{
HeapAdjust(k,i,n);
}
for(i=n;i>1;i--)
{
swap(k,1,i);
HeapAdjust(k,1,i-1);
}
}
int main()
{
int i,a[10]={-1,5,2,6,0,3,9,1,7,4};
HeapSort(a,9);
printf("jieguo:");
for(i=1;i<10;i++)
{
cout<<a[i];
}
return 0;
}
- 初始化建堆的时间复杂度为O(n),排序重建堆的时间复杂度为nlog(n),所以总的时间复杂度为O(n+nlogn)=O(nlogn)。另外堆排序的比较次数和序列的初始状态有关,但只是在序列初始状态为堆的情况下比较次数显著减少,在序列有序或逆序的情况下比较次数不会发生明显变化。
- 基本操作:
- 查找最大值O(1)
- 插入一个数O(logn)
- 删除一个数O(logn)
- 修改一个数O(logn)
但是如果是c++ stl中的堆priority_queue,则不能修改一个数,而且只能删除堆顶元素。
堆的两个基本操作down()和up().
高精度
一般四种
- 大整数相加
- 大整数乘以比较小的数(大整数的位数为10的6次方,小整数大概为10的9次方大小)
- 大整数除以一个小整数
- 两个大整数相减
- 用数组来存储,个位在数组的前面
高精度加法
代码实现
#include<iostream>
#include<vector>
using namespace std;
const int N=1e6+10;
vector<int> add(vector<int> &A,vector<int> &B)
{
vector<int> C;
int t=0;
for(int i=0;i<A.size()||i<B.size();i++)
{
if(i<A.size()) t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t) C.push_back(t);
return C;
}
int main()
{
string a,b;
vector<int> A,B;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
auto C=add(A,B);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
高精度减法
代码实现
#include<iostream>
#include<vector>
using namespace std;
bool cmp(vector<int> &A,vector<int> &B)
{
if(A.size()!=B.size()) return A.size()>B.size();
for(int i=A.size()-1;i>=0;i--)
{
if(A[i]!=B[i])
return A[i]>B[i];
}
return true;
}
vector<int> sub(vector<int> &A,vector<int> &B)
{
vector<int> C;
int t=0;
for(int i=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size()) t-=B[i];
if(t>=0)
{
C.push_back(t);
t=0;
}
else
{
C.push_back(t+10);
t=1;
}
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
int main()
{
string a,b;
vector<int> A,B;
cin>>a>>b;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
if(cmp(A,B))
{
auto C=sub(A,B);
int j=C.size()-1;
for(int i=j;i>=0;i--) printf("%d",C[i]);
}
else
{
auto C=sub(B,A);
printf("-");
int j=C.size()-1;
for(int i=j;i>=0;i--)
{
printf("%d",C[i]);
}
}
return 0;
}
- ios::sync_with_stdio(false): C++中&