排序算法汇总
来源:互联网 发布:b2c商城网站源码下载 编辑:程序博客网 时间:2024/06/08 02:58
排序:
以下排序都是按从小到大的顺序编写的;
1)直接插入排序:
时间复杂度O(n*n)
//从小到大//直接插入排序:取出一个比,比完再插入void InsertSort(int* arr, size_t n){ assert(arr); for(size_t i = 0 ; i < n-1; i++) { int temp = arr[i+1]; int j = i; for(; j >= 0; --j) { if(arr[j] > temp) { arr[j+1] = arr[j]; } else break; } arr[j+1] = temp; }}
2)希尔排序:预排序+插入排序
类似于插入排序的,只不过是跨步比(+gap),是插入排序的一种优化;
预排序:尽可能的将大数放后面,小数放前面,不是完全有序的,当gap越小越接近于有序,当gap==1时就是插入排序,当gap>1是希尔排序;
//希尔排序//时间复杂度:O(n)< O <O(n*n)void shellSort(int* arr,size_t n){ assert(arr); int gap = 3; while(gap > 1) { gap = gap/3 + 1; for(size_t i = 0; i < n-gap; i++) { int temp = arr[i+gap]; int j = i; for(; j >= 0; j -= gap) { if(arr[j] > temp) { arr[j+gap] = arr[j]; } else break; } arr[j+gap] = temp; } }}
3)选择排序:
选择排序是最坏的排序;
时间复杂度是:O(n*n)
//选择排序----最坏的排序,这里我们一次性选出最大最小值void selectSort(int* arr,size_t n){ size_t begin = 0; size_t end = n-1; while(begin < end) { int min = begin; int max = begin; for(size_t i = begin+1; i <= end; i++) { if(arr[i] < arr[min]) { min = i; } if(arr[i] > arr[max]) { max = i; } } swap(arr[min],arr[begin]); //如果此时最大值就是begin下标的那个值, //经过上一步的交换,最大值被换到了下标是min的位置上去了 if(begin == max) { max = min; } swap(arr[max],arr[end]); ++begin,--end; }}
4)堆排序:分为大堆和小堆
时间复杂度是:O(n*lgn)
//建大堆,先把最大的值放在上面,然后再让最大值和最后一个值交换,然后再进行向下调整,再把第二大放在最上面,依次类推;
//从小到大//堆排序----建大堆void AdjustDown(int* arr,size_t n,int father){ int childL = 2*father+1; while(childL < n) { if((childL+1) < n && arr[childL+1] > arr[childL]) { childL++; } if(arr[father] < arr[childL]) { swap(arr[father],arr[childL]); father = childL; childL = 2*father+1; } else break; }}void HeapSort(int* arr,size_t n){ assert(arr); for(int i = (n-2)/2; i >= 0; --i) { AdjustDown(arr,n,i); } //目前最大值在顶层 int end = n-1; while(end > 0) { swap(arr[0],arr[end]); //调整 AdjustDown(arr,end,0); --end; }}
5)冒泡排序;
这应该是我们最熟悉的排序吧,不做特别说明直接见代码:
//冒泡排序void BubbleSort(int* arr,size_t n){ assert(arr); for(size_t j = 0; j < n-1; j++) { //单趟 int heap = 0; for(size_t i = 0; i < n-1; i++) { if(arr[i] > arr[i+1]) { heap = 1; swap(arr[i],arr[i+1]); } } if(heap == 0) { break; } }}
6)快速排序:—-递归过程
A.左右指针法:
左边找大于key的,右边找小于key的
key就是最右边那个值(实际值);
begin和end是下标
//左右指针法int PartSort(int* arr,int begin,int end){ int right = end; int key = arr[end]; while(begin < end) { //先判断左指针 while(arr[begin] < key && begin < end) { ++begin; } //再判断右指针 while(arr[end] >= key && begin < end) { --end; } //交换两个指针找到的值 swap(arr[begin],arr[end]); } swap(arr[begin],arr[right]); return begin;}
B.挖坑法:(和左右指针法类似,只不过这次是将选中的那个数挖走交换)
//挖坑法int PartWSort(int* arr,int begin,int end){ int newkey = GetMidKey(arr,begin,end); /*int pit = end;*/ swap(arr[newkey],arr[end]); int key = arr[end]; while(begin < end) { while(arr[begin] < key && begin < end) { ++begin; } /*if(begin < end) { arr[pit] = arr[begin]; pit = begin; }*/ arr[end] = arr[begin]; while(arr[end] >= key && begin < end) { --end; } /*if(begin < end) { arr[pit] = arr[end]; pit = end; }*/ arr[begin] = arr[end]; } /*arr[pit] = key;*/ arr[end] = key; return begin;}
C.前后指针法:
一前一后两个指针:
cur走到9的时候,9>key,++cur(多走一步),所以++prev走到9的位置,prev != cur,所以交换5和9:
//前后指针法int PartPBSort(int* arr,int begin,int end){ assert(arr); int indexKey = GetMidKey(arr,begin,end);//返回的是下标 /*int key = arr[end];*/ swap(arr[indexKey],arr[end]); int key = arr[end]; int cur = begin; int prev = cur - 1; while(cur < end) { if(arr[cur] < key && ++prev != cur) { swap(arr[prev],arr[cur]); } ++cur; } swap(arr[end],arr[++prev]); return prev;}void QuickSort(int* arr,int begin,int end){ assert(arr); if(begin < end) { int div = PartWSort(arr,begin,end); /* int div = PartPBSort(arr,begin,end);*/ QuickSort(arr,begin,div-1); QuickSort(arr,div+1,end); }}
但是快速排序也有缺陷:
Eg:2 3 230 32 2 3 3 3 32 2 2 3 3 233
以上就无法用快排,降低了快速排序的性能;如果每次选中的数据是最大/最小值,那么就是N*N的复杂度;
所以要避免每次选取key的时候选择最大、最小值特有以下解决方法:
① 三数取中法:每次都给出最左边和最右边的值(下标),然后求中间值(下标),再比较三个数大小(实际值),找出中间值(实际值)的作为key,返回的是下标;
//三数取中法---取键值int GetMidKey(int* arr,int begin,int end){ assert(arr); int mid = begin + ((end - begin) >> 1); if(arr[begin] < arr[mid]) { if(arr[begin] > arr[end]) { return begin; } else if(arr[mid] < arr[end]) { return mid; } else { return end; } } else { //arr[begin] >= arr[mid] if(arr[mid] > arr[end]) { return mid; } else if(arr[begin] > arr[end]) { return end; } else { return begin; } }}
② 小区间优化快排法:其实就是当你将要排序的小区间的长度小于某个固定值(这里我们设为10)时,采用插入排序法,如果不是就还是采用快排的方式排序;
//小区间优化法快排void MinizoneQuickSort(int* arr,int begin,int end){ assert(arr); int size = end - begin; if(size <= 0) { return; } else if(size < 10) { InsertSort(arr+begin,size+1); } else { int div = PartSort(arr,begin,end); MinizoneQuickSort(arr,begin,div-1); MinizoneQuickSort(arr,div+1,end); }}
③ 非递归快排;(利用stack的结构)
其实思想和快排的递归思想一致,只不过我们需要创建一个栈stack,来模拟实现递归过程的压栈过程,我们压下标即可;
//非递归快排void QuickSortNonR(int* arr,int begin,int end){ assert(arr); stack<int> _stack; if(begin < end) { _stack.push(end);//放进去的是下标 _stack.push(begin); } while(!_stack.empty()) { int left = _stack.top(); _stack.pop(); int right = _stack.top(); _stack.pop(); int div = PartSort(arr,left,right); if(div+1 < right) { _stack.push(right); _stack.push(div+1); } if(left < div-1) { _stack.push(div-1); _stack.push(left); } }}
7)归并排序:
思想:递归切分,先将begin和end区间逐个切分到一个区间就一个数,然后开始回退比较,这样生成的左右两边有序小区间排序,生成一个大一点的有序小区间后,继续向上回退,再左右两边排序,这样一直回退到顶端;
这种思想适合大量数据排序,时间复杂度是N*lgN,空间复杂度为O(N);这是一种外排序——–>磁盘上排序(如果内存放不下就放在磁盘上)
//归并排序----外排序----磁盘void _Sort(int* arr,int* temp,int begin1,int end1,int begin2,int end2){ int start = begin1; int finish = end2; int index = begin1; while(begin1 <= end1 && begin2 <= end2) { if(arr[begin1] > arr[begin2]) { temp[index++] = arr[begin2++]; } else { temp[index++] = arr[begin1++]; } } while(begin1 <= end1) { temp[index++] = arr[begin1++]; } while(begin2 <= end2) { temp[index++] = arr[begin2++]; } memcpy(arr+start,temp+start,sizeof(int)*(finish - start + 1));}//分成小区间void _MergeSort(int* arr,int* temp,int begin,int end){ if(begin >= end) { return; } int mid = begin + ((end - begin) >> 1); _MergeSort(arr,temp,begin,mid); _MergeSort(arr,temp,mid+1,end); _Sort(arr,temp,begin,mid,mid+1,end);}void MergeSort(int* arr,int n){ assert(arr); int* temp = new int[n];//先创建一个临时变量,并开辟空间 _MergeSort(arr,temp,0,n-1); delete [] temp;//自己申请的空间用完以后要自己释放}
8)并查集:只针对下标(缺陷)
将N个不同的元素分成一组不相交的集合;
开始时每个元素都是一个集合,然按照规律将两个集合进行合并
也就是说会将与集合中某些元素相关的多个小集合合并成大集合,比如说朋友圈,{1,3},{2,3},{3,4},{5,6}——->{1,2,3,4},{5,6}这样的,我们同通过如下方法分配他们之间的关系:
分析过程如下:
//并查集class UnionFindSet{protected: vector<int> _ufs;public: UnionFindSet(size_t n) { _ufs.resize(n,-1); } //注:每次放入的一组数据一定是一个集合; void SetUnion(int x1,int x2) { int root1 = _FindRoot(x1); int root2 = _FindRoot(x2); if(root1 != root2) { if(_ufs[root1] == -1) { swap(root1,root2); } _ufs[root1] += _ufs[root2]; _ufs[root2] = root1; } } //判断是否是一个集合 bool IsOneUnion(int x1,int x2) { return _FindRoot(x1) == _FindRoot(x2); } //判断有几个根 int GetRootCount() { size_t count = 0; for(size_t i = 0; i < _ufs.size(); i++) { if(_ufs[i] < 0) { ++count; } } return count; }private: int _FindRoot(int x) { int root = x; if(_ufs[x] >= 0) { root = _ufs[x]; } return root; }};int friends(int n,int m,int r[][2]){ UnionFindSet ufs(n+1); for(size_t i = 0; i < m; i++) { ufs.SetUnion(r[i][0],r[i][1]); cout<<ufs.IsOneUnion(r[i][0],r[i][1])<<" "; } return ufs.GetRootCount()-1;//去掉_ufs[0]}void TestUnion(){ int n = 6; const int m = 4; int r[m][2] = {{1,3},{2,3},{3,4},{5,6}}; cout<<friends(n,m,r)<<endl;}
- 排序算法--排序算法汇总
- 排序算法--排序算法汇总
- 排序算法汇总
- JAVA排序算法汇总
- java排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 排序算法汇总
- 各种排序算法汇总
- 排序算法汇总
- java排序算法汇总
- 基本排序算法汇总
- 算法---排序汇总
- java 排序算法汇总
- react native 在设备在运行时的问题
- Groovy学习记录-------Groovy安装/配置
- ansible安装
- EasyUI 学习与总结
- Elasticsearch系列(二)----Elasticsearch 基本使用
- 排序算法汇总
- apache的基本配置
- 大家都懂的 JSON 解析器原理(一)简介 & 低配版入门
- oracle权限不足的问题
- Java中“引用”的几种类型
- 使用ajax跨域withCredentials的作用
- 第10章
- 回眸一笑---走在自己的时区里
- RNN Summarization