排序的学习 | 青训营笔记
1.插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动 。
代码实现:
void insert_sort(*vector*<int> &num)
{
for (int i = 0; i < num.*size*() - 1; ++i)
{
int end = i;
int n =num[end + 1];
while (end >= 0)
{
if (n < num[end])
{
num[end + 1] = num[end];
end--;
}
else
{
break;
}
}
num[end + 1] = n;
}
}
时间复杂度:
最坏情况下为O(N^2),此时待排序列为逆序,或者说接近逆序
最好情况下为O(N),此时待排序列为升序,或者说接近升序。
空间复杂度:O(1)
2.快速排序
快速排序(Quicksort),计算机科学词汇,适用领域Pascal,c++等语言,是对冒泡排序算法的一种改进。
快速排序算法通过多次比较和交换来实现排序,其排序流程如下
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
基本思想:
插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较。拿在左手上的牌总是排序好的,原来这些牌是桌子上牌堆中顶部的牌 。
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序 。
代码:
1. 递归
void quick1sort(int n[],int l,int r)
{
if (l >= r)return;
int i = l, j = r;
int num = n[l];
while (i < j)
{
while (n[j] >= num && j > i)
{
--j;
}
n[i] = n[j];
while (n[i] <= num && i < j)
{
++i;
}
n[j] = n[i];
}
n[i] = num;
quick1sort(n, l, i - 1);
quick1sort(n, i + 1, r);
}
2. 非递归
int quick1(int n[],int l,int r)
{
int i = l, j = r;
int num = n[l];
while (i < j)
{
while (n[j] >= num && j > i)
{
--j;
}
n[i] = n[j];
while (n[i] <= num && i < j)
{
++i;
}
n[j] = n[i];
}
n[i] = num;
return i;
}
void quick_sort(int n[], int l, int r)
{
*stack*<int> st;
st.*push*(r); st.*push*(l);
while (!st.*empty*())
{
int i = st.*top*();
st.*pop*();
int j = st.*top*();
st.*pop*();
int mid = quick1(n,i,j);
if (i < mid - 1)
{
st.*push*(mid-1);
st.*push*(i);
}
if (j > mid + 1)
{
st.*push*(j);
st.*push*(mid + 1);
}
}
}
时间复杂度:
最佳情况:O(nlogn)
最差情况:O(n2)
平均情况:O(nlogn)
3.堆排序
堆排序是指利用堆这种数据结构所设计的一种排序算法。 堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。
堆排序中,首先要建立一个堆,通过一个数组来构建一个完全二叉树,然后将这棵树构建为一个大顶堆或者小顶堆,然后来通过堆来进行对元素的排序。
堆排序(从小到大)按照以下步骤:
最大堆调整:将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆:将堆中的所有数据重新排序
堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算
代码:
void heapify(int num[], int n, int i)
{
int largest = i;
int left = 2 * i + 1;
int right = i * 2 + 2;
if (left < n && num[largest] < num[left])
{
largest = left;
}
if (right < n && num[largest] < num[right])
{
largest = right;
}
if (largest != i)
{
swap(num[largest], num[i]);
heapify(num, n, largest);
}
}
void heap_sort(int arr[], int n)
{
//建堆
int lastNode = n - 1; //从后往前建堆
int parent = (lastNode - 1) / 2;
for (int i = parent; i >= 0; i--)
{
heapify(arr, n, i);
}
for (int i = n - 1; i >= 0; i--)
{
swap(arr[i], arr[0]);
heapify(arr, i, 0);
}
}
时间复杂度:
最佳情况:O(nlogn)
最差情况:O(nlogn)
平均情况:O(nlogn)
———————————————————————————————————————————
以上就是学习到的三个排序算法。