1,排序的概念及其应用
概念:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.
常见的排序算法:
2,常见排序算法的实现
1.插入排序:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,知道所有的记录插入完为止,得到一个新的有序序列.
(1)直接插入排序:
在插入第i个元素时,与前面已经排好的元素进行比较,找到插入位置插入,原来位置上的元素后移。
代码实现:
//插入排序
void InsertSort(int array[],int size)
{
//逐个取出待排序的元素
for (int i = 1;i<size;++i)
{
int key = array[i];
//取出i号元素与他前面的数据进行比较,寻找插入位置
int end = i - 1;
while (key < array[end] && end>=0)
{
array[end + 1] = array[end];
--end;
}
//插入元素
array[end + 1] = key;
}
return;
}
特性总结:
1.元素越接近有序,直接插入排序算法的时间效率越高
2.时间复杂度:O(N^2)
3.空间复杂度:O(1)
3.稳定性:稳定
(2)希尔排序
基本思想:先选定一个整数,然后把元素分组,对每组内的元素进行排序。然后缩小这个整数,重复。直到为1时,排好序;
代码实现:
//希尔排序
void ShellSort(int a[],int size)
{
int gap = size / 3 + 1;
//逐个取出待排序的元素
while (gap > 0)
{
for (int i = gap; i < size; ++i)
{
int key = a[i];
//取出i号元素与他前面的数据进行比较,寻找插入位置
int end = i - gap;
while (key < a[end] && end >= 0)
{
a[end + gap] = a[end];
end -= gap;
}
//插入元素
a[end + gap] = key;
}
gap /= 2;
}
}
希尔排序的特性总结:
1,希尔排序是对直接插入排序的优化
2,当gap>1时都是预排序,目的是让数组更接近有序。当gap==1时,数组已经接近有序,这样就会很快,就整体而言,这样可以达到优化的效果。
3,希尔排序的时间复杂度需要推导,大约为O(N^1.3),
4,稳定性:不稳定
2.选择排序:
基本思想:每一次从待排序的元素中选出最小的(或最大的一个元素),存放在序列的起始位置,直到全部排序的数据元素排完。
(1)直接选择排序:
<1>在元素集合a[i]–a[n-1]中选择关键码最大(小)的数据元素
<2>若他不是这组数据最后一个(最前一个)元素,则与这组元素最后一个(最前一个)交换。
<3>在剩余的啊[i]–a[n-2]中重复上述操作;
代码实现:
//选择排序
void SelectSort(int a[], int size)
{
while (size > 0)
{
int max = 0;
for (int i = 1; i < size; ++i)
{
if (a[i] > a[max])
max = i;
}
Swap(&a[max], &a[size - 1]);
--size;
}
}
直接选择排序特性总结:
1.效率不好,实际中很少用到
2.时间复杂度O(N^2)
3.空间复杂度O(1)
4.稳定性:不稳定
(2)堆排序
堆排序是指利用堆这种数据结构设计的一种排序算法,它是通过堆来进行选择数据
代码实现:
//堆排序
void HeapAdjust(int* array, int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
//找左右孩子中较大的孩子
if (child + 1 < size && array[child + 1] > array[child])
child += 1;
if (array[child] > array[parent])
{
Swap(&array[child], &array[parent]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void HeapSort(int* array, int size)
{
//找到第一个非叶子结点
int root = ((size - 2) >> 1);
for (; root >= 0; --root)
HeapAdjust(array, size, root);
//2,排序:删除方式
int end = size - 1;
while (end)
{
Swap(&array[0], &array[end]);
HeapAdjust(array, end, 0);
end--;
}
}
堆排序特性总结:
1,时间复杂度:O(N*logN)
2,空间复杂度:O(1)
3,稳定性:不稳定
3,交换排序:
基本思想:就是根据序列中两个记录键值的比较结果来对换这两个纪录在序列中的位置
(1)冒泡排序:
//冒泡排序
void BubbleSort(int a[], int size)
{
for (int i = 0; i < size-1; ++i)
{
for (int j = 0; j < size-i-1; ++j)
{
if (a[j] > a[j+1])
Swap(&a[j], &a[j+1]);
}
}
}
冒泡排序优化:
void BubbleSort1(int a[], int size)
{
for (int i = 0; i < size-1; ++i)
{
//用flag来限制后面已经排好的数,不再进行排序
int flag = 1;
for (int j = 0; j < size-i-1; ++j)
{
if (a[j] > a[j+1])
{
Swap(&a[j], &a[j+1]);
flag = 0;
}
}
if (flag == 1)
break;
}
}
冒泡排序再优化:
void BubbleSort2(int a[], int size)
{
int cur = size - 1;
for (int i = 0; i < size-1; ++i)
{
int flag = 1;
int num = size - i - 1;
//用cur减少循环,后面排好的数不在进行循环排序
for (int j = 0; j < cur; ++j)
{
if (a[j] > a[j + 1])
{
Swap(&a[j], &a[j + 1]);
flag = 0;
num = j;
}
}
cur = num;
if (flag)
break;
}
}
冒泡排序特性总结:
1,时间复杂度:O(N^2)
2,空间复杂度:O(1)
3,稳定性:稳定
(2)快速排序:
基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中的值均小于基准值,右子序列中得值均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相信位置上。
代码实现:
将区间划分为2部分的常见方式:
1,hoare版本:
//快速排序
int Pation1(int* array, int left, int right)
{
int key = array[right - 1];
int begin = left;
int end = right - 1;
while (begin < end)
{
//让begin从前往后找比基准值大的元素
//让end从后往前找比基准值小的元素
while (begin < end && array[begin] <= key)
++begin;
while (begin < end && array[end] >= key)
--end;
if (begin < end)
Swap(&array[begin], &array[end]);
}
Swap(&array[begin], &array[right - 1]);
return begin;
}
2,挖坑法:
//挖坑法
int Pation2(int* array, int left, int right)
{
int begin = left;
int end = right - 1;
int key = array[end];
while (begin < end)
{
//让begin从前往后找比基准值大的元素
while (begin < end && array[begin] <= key)
++begin;
//用begin位置的元素填end位置的坑
if (begin < end)
{
array[end] = array[begin];
--end;
}
//让end从后往前找比基准值小的元素
while (begin<end && array[end]>=key)
--end;
if (begin < end)
{
array[begin] = array[end];
++begin;
}
}
array[begin] = key;
return begin;
}
3,前后指针:
//前后指针
int Pation3(int* array, int left, int right)
{
int cur = left;
int prev = cur - 1;
int key = array[right - 1];
while (cur < right)
{
if (array[cur] < key && cur != ++prev)
{
Swap(&array[cur], &array[prev]);
}
++cur;
}
++prev;
Swap(&array[prev], &array[right - 1]);
return prev;
}
快速排序递归:
//[left,right)
void QuickSort(int* array, int left, int right)
{
if (right - left > 1)
{
int mid = Pation1(array, left, right);
//[left,mid)
QuickSort(array, left, mid);
//[mid+1,right)
QuickSort(array, mid + 1, right);
}
}
循环:
//循环
void QuickSort(int* array,int size)
{
int left = 0;
int right = size;
stack<int> s;
s.push(right);
s.push(left);
while (!s.empty())
{
left = s.top();
s.pop();
right = s.top();
s.pop();
if (right - left > 1)
{
int mid = Pation1(array, left, right);
//[mid+1,right)
s.push(right);
s.push(mid + 1);
//[left,mid)
s.push(mid);
s.push(left);
}
}
}
快排特性总结:
1,时间复杂度:O(N*logN) (最优) O(N^2) (最差)
2,空间复杂度O(logN)
3,稳定性:不稳定
归并排序:
基本思想:先使子序列有序,再使子序列段间有序。就是将序列分为两部分,使这两部分都有序,在合并。
代码实现:
//归并排序
void MergeData(int* array,int left,int mid,int right,int* temp)
{
int begin1 = left, end1 = mid;
int begin2 = mid, end2 = right;
int index = left;
while (begin1 < end1 && begin2 < end2)
{
if (array[begin1] <= array[begin2])
temp[index++] = array[begin1++];
else
temp[index++] = array[begin2++];
}
while(begin1<end1)
temp[index++] = array[begin1++];
while(begin2<end2)
temp[index++] = array[begin2++];
}
递归的方式:
void _MergeSort(int* array, int left, int right, int* temp)
{
if (right - left > 1)
{
int mid = left + ((right - left) >> 1);
_MergeSort(array, left, mid, temp);
_MergeSort(array, mid, right, temp);
MergeData(array, left, mid, right, temp);
memcpy(array + left, temp + left, (right - left) * sizeof(array[0]));
}
}
void MergeSort(int* array, int size)
{
int* temp = new int[size];
_MergeSort(array, 0, size, temp);
delete[] temp;
}
循环的方式:
//循环
void _MergeNor(int* array, int size)
{
int* temp = new int[size];
int gap = 1;
while (gap<size)
{
for (int i = 0; i < size; i += 2 * gap)
{
int left = i;
int mid = left + gap;
if (mid >= size)
mid = size;
int right = mid + gap;
if (right >= size)
right = size;
MergeData(array, left, mid, right, temp);
}
memcpy(array, temp, size * sizeof(array[0]));
gap *= 2;
}
}
归并排序特性总结:
1,时间复杂度:O(N*logN)
2,空间复杂度:O(N)
3,稳定性:不稳定