今天来说一下排序,我平时利用的排序多数是STL的sort排序,很少自己去写排序算法,但是既然是知识点总结,那肯定就少不了排序算法,所以今天把我自己整理的一些写过的算法和大家一起分享一下,望各位批评指正。
c++排序算法知识点总结:
1.排序算法是否稳定:
在排序算法执行的时候,当a,b相等的时候,若对a和b进行交换则不稳定,反之则是稳定的
2.时间复杂度与空间复杂度:
时间复杂度:对于排序数据的总的操作次数,反应当n(排序数据量)变化时,操作次数呈现什么规律
空间复杂度:是指排序算法在计算机内执行时,所需存储空间的度量,也是数据规模n的函数
3.排序方法解释及代码实现:
a.快速排序
快速排序是一种不稳定排序,也就是说在遇到相等项的时候位置可能会发生变化
时间复杂度:平均O(n*logn) 最坏O(n^2)
空间复杂度:O(1)
快速排序算法思想:快速排序采用的算法思想是一种分治思想,通过递归的调用来对数组循环进行排序
例:若是相对此数组进行排序int a[]={34,65,12,43,67,5,78,10,3,70}:
此数组array 数组量10 int len = 10;
1.第一次比较 假定a[0] 为基准数 定义int K = -1;K = a[0];
定义循环变量int i = 0,j = Len - 1;
开始比较:从前开始向后找比K值大的第一个数值找到了 a[1] a[0] = a[1]; 此时a[1] 可以被视为是一个坑 那么现在就要把这个坑填好
从后向前找比K值小的数值 找到了a[8] a[1] = a[8]
此时 i,j数值都发生了变化K依然是34 此时继续重复之前的步骤 直到i >= j时退出 此时将标记变量放置在a[i]中 自此a[i]左侧都是比他大的 右侧都是比他小的
2.开始递归循环左右两侧 左侧0到i 右侧i+1到Len-1 继续执行上述步骤
代码实现:vs2015 亲测可用
主要介绍:冒泡、快速、选择、归并
#include <iostream>
using namespace std;
//交换函数 采用后一种方式 比较高大上,这也是面试题中的一种,不采用中间变量交换数据
void Swap(int& a,int& b)
{
//此时容易出现一个bug 就是当需要交换的值是同一个位置,即:一个值,而此时又是传递的引用 可能最后结果为零需要注意
/*a = a + b;
b = a - b;
a = a - b;*/
int temp = a;
a = b;
b = temp;
}
//冒泡排序
void BubbleSort(int a[],int Len)
{
//为了保证程序的健壮性 需要加以判空
if (a == NULL)
return;
//最外层循环次数,因为是从前向后开始排列,因此最后一次应该为小于(Len-1),即(Len-2),保证了a[Len-2]与a[Len-1]的最后一次比较
for (int i = 0;i < Len -1;i++)
{
//从前向后开始升序排列 因为此时最大的数据已经进入最后部分,因此后续排列不用考虑最后部分,为了提高效率采用(Len-1-i)的形式
for (int j = 0; j < Len - 1 - i; j++)
{
if (a[j] > a[j + 1])
Swap(a[j], a[j + 1]);
}
}
}
//选择排序
/*选择排序其实与冒泡排序类似,找到大值或者小值后,将索引进行标记,通过引用的方式对两个值进行引用传递*/
void SelectSort(int a[10],int len)
{
//索引标记变量
int k = -1;
//两层循环遍历
for (int i = 0;i < len-1;i++)
{
k = i;
for (int j = i;j < len-1;j++)
{
if (a[j+1] < a[k])
{
k = j + 1;
}
}
Swap(a[k], a[i]);
}
}
//快速排序
void quickSort(int s[], int l, int r)
{
if (l < r)
{
int i = l, j = r, x = s[l];
while (i < j)
{
while (i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if (i < j)
s[i++] = s[j];
while (i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if (i < j)
s[j--] = s[i];
}
s[i] = x;
quickSort(s, l, i - 1); // 递归调用
quickSort(s, i + 1, r);
}
}
// 定义三个宏,分别用于求左孩子/右孩子/父结点的下标。
#define LEFT(i) (((i) << 1) + 1)
#define RIGHT(i) (((i)+1) << 1)
#define PARENT(i) (((i)-1) >> 1)
// 小于比较函数
bool less(int lhs, int rhs)
{
return lhs < rhs;
}
// 大于比较函数
bool greate(int lhs, int rhs)
{
return lhs > rhs;
}
typedef bool(*Comp)(int, int);
// 假设一个节点的左子树与右子树都满足堆的性质,而该节点不满足最大堆或最小堆的性质,该
// 函数实现调整节点位置,维护堆的性质。
// 输入参数分别表示: 堆的数组/数组长度/节点i的下标/表示比较的二元谓词
// 复杂度为O(logN).
void Heapify(int array[], int nLength_, int nIndex_, Comp CompFunc)
{
if (array == nullptr || nIndex_ >= nLength_ || nIndex_ < 0 || CompFunc == nullptr)
return;
int _nLeft = LEFT(nIndex_);
int _nRight = RIGHT(nIndex_);
// 初始化最大值节点的下标;
int _nLargest = nIndex_;
if (_nLeft < nLength_ && !CompFunc(array[_nLargest], array[_nLeft]))
{
_nLargest = _nLeft;
}
if (_nRight < nLength_ && !CompFunc(array[_nLargest], array[_nRight]))
{
_nLargest = _nRight;
}
/* 此时不需要维护堆的性质,直接返回 */
if (_nLargest == nIndex_)
{
return;
}
swap(array[nIndex_], array[_nLargest]);
Heapify(array, nLength_, _nLargest, CompFunc);
}
// 使用一个数组建立一个最小堆或最大堆。
// 输入参数为:一维数组/数组的长度/表示比较的二元谓词,用于控制建最小堆还是最大堆
// 复杂度为O(N).
void BulidHeap(int array[], int nLength_, Comp CompFunc)
{
if (array == nullptr || nLength_ <= 1 || CompFunc == nullptr)
return;
// 从最后一个元素的父节点开始调用Heapify()函数来建堆。
for (int i = PARENT(nLength_ - 1); i >= 0; --i)
{
Heapify(array, nLength_, i, CompFunc);
}
}
// 堆排序的函数
// 说明:1. 通过建立[最大堆]可以实现[从小到大]的排序;
// 2. 通过建立[最小堆]可以实现[从大到小]的排序
// 3. 堆排序为原址排序,它不需要借助额外的空间.(归并排序不是原址排序)
// 4. 堆排序的复杂度为O(NlogN).
//
// 堆排序的思想 (以从小到大排序说明):
// 第一步:建立一个最大堆;
// 第二步:把首元素与最后一个元素进行交换;
// 第三步:把堆的大小减1,对新的堆进行维护维的性质;
// 第四步:把首元素与倒数第二个元素进行交换;
// 第五步:把堆的大小减1,对新的堆进行维护维的性质;
// .......
//
void HeapSort(int array[], int nLength_, Comp CompFunc)
{
if (array == nullptr || nLength_ <= 1 || CompFunc == nullptr)
return;
BulidHeap(array, nLength_, CompFunc);
for (int i = nLength_; i >= 2; /* 循环内 */) // i表示当前堆的大小
{
swap(array[0], array[--i]);
Heapify(array, i, 0, CompFunc);
}
}
int _count = 0;//逆序对的个数
//函数作用:合并[left,mid][mid+1,right]
void Merge(int a[],int left,int mid,int right)
{
//两段区间的长度
int length1 = mid-left+1;
int length2 = right-mid;
//分配两段新的内存空间存储原内容
int *l1 = new int[length1];
int *l2 = new int[length2];
for (int i = 0; i < length1; ++i)
{
l1[i] = a[left+i];
}
for (int j = 0; j < length2; ++j)
{
l2[j] = a[j+mid+1];
}
//存入原内容之后,比较两个序列
int i = 0,j = 0;
int k = length1;
//比较两个序列的重合部分,进行排序
while (i<length1 && j<length2)
{
if (l1[i] < l2[j])
{
a[left++] = l1[i++];
}
else
{
a[left++] = l2[j++];
//因为l2[j]大于l1[i],所以l2[j]肯定大于[0,length-1]之中[0,i]之间的所有数,产生逆序对
if (l2[j] > l1[i])
{
_count += length1-i+1;
}
}
}
//两序列的剩余部分分别放于结尾
while (i<length1)
{
a[left++] = l1[i++];
}
while (j<length2)
{
a[left++] = l2[j++];
}
//分配的内存需要释放掉
delete []l1;
delete []l2;
}
void Merge_sort(int a[],int left,int right)
{
if (left < right)
{
int mid = (left+right)/2;
//首先进行分区,然后递归操作
Merge_sort(a,left,mid);
Merge_sort(a,mid+1,right);
//第一次将其分为两个区间,进行合并操作
Merge(a,left,mid,right);
}
}
int main()
{
int a[] = {3,10,1,20,2,6,4,5,8,7};
//int 为四字节 所以除以四
int Count = sizeof(a) / 4;
//时间复杂度O(n²) 空间复杂度O(1)
//BubbleSort(a, Count);
//时间复杂度O(n²) 空间复杂度O(1)
//SelectSort(a, Count);
//时间复杂度O(nlogn) 空间复杂度O(logn)
//quickSort(a, 0, 9);
//时间复杂度O(nlogn) 空间复杂度O(1)
//HeapSort(a, 10, greate);
//时间复杂度O(nlogn) 空间复杂度O(n)
Merge_sort(a, 0, 9);
//打印数组
for (int i = 0;i < Count;i++)
{
printf("%d ",a[i]);
}
system("pause");
return 0;
}