[排序的学习 | 青训营笔记]

105 阅读2分钟

排序的学习 | 青训营笔记

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)

———————————————————————————————————————————

以上就是学习到的三个排序算法。