面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第1张图片

一、冒泡排序

原理与图示

  • 这是一种简单的排序方法,它使用一种“冒泡策略”把最大元素移到序列最右端
  • 在一次冒泡过程中,每相邻的元素比较,如果左边的元素大于右边的元素,则交换。每比较一轮,找出一位最大数,然后移动到序列的最右端。例如下图是一次冒泡排序的过程

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第2张图片

  • 下面给出了一个总的冒泡过程,每一行表示一次冒泡过程(省略了交换的细节):

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第3张图片

代码如下

template
void BubbleSort(T a[], int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        for (int j = 0; j < n - 1 - i; ++j)
        {
            if (a[j] > a[j + 1])
            {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
                //这三个步骤也可以使用swap(arr[j],arr[j+1]);代替
            }
        }
    }
}
  • 测试代码如下:
int main()
{
    int a[] = { 6,5,8,4,3,1 };
    BubbleSort(a, 6);
    for (const auto& elem : a)
        std::cout << elem << " ";
}

 

二、插入排序

原理与图示

  • 原理: 首先认为首元素有序,然后从剩余的数据中取出第一个元素与前面的序列进行比较排序,放入到序列中,然后继续取下一个元素...以此类推
  • 例如,下面用白色部分表示有序段,阴影部分表示无序段。插入排序的规则是:
    • 第1行排序时,6放在最前面
    • 第2行排序时,将无序段的第一个元素5插入到前面白色的有序段中,结果如第2行所示
    • 第3行排序时,将无序段的第一个元素8插入到前面白色的有序段中,结果如第3行所示
    • ......以此类推,每次从无序段阴影部分挑选出第一个元素,然后插入到前面的白色部分有序段中

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第4张图片

  • 备注:当然,你可以可以认为后面有序,前面无序,而每次从前面无序段中插入元素到后面得以有序段中,自己实现,原理是相同的

代码

  • 下面两种方式都可以
template
void insertionSort(T a[], int n)
{
    //从第2个元素开始遍历
    for (int i = 1; i < n; ++i)
    {
        T temp = a[i];
        T j;
        //只要temp比前面有序段中的元素小,那么就将有序段中的元素后移
        for (j = i - 1; j >= 0 && a[j] > temp; --j)
            a[j + 1] = a[j];
        a[j+1] = temp;
    }
}
//将elem插入到a所在的数组中
template
void insert(T a[], int n, const T& elem)
{
    int i;
    for (i = n - 1; i >= 0 && a[i] > elem; --i)
        a[i + 1] = a[i];
    a[i + 1] = elem;
}

template
void insertionSort(T a[], int n)
{
    for (int i = 1; i < n; ++i)
    {
        T t = a[i];
        insert(a, i, t);
    }		
}

测试代码如下:

int main()
{
    int a[] = { 6,5,8,4,3,1 };
    insertionSort(a, 6);
    for (const auto& elem : a)
        std::cout << elem << " ";
}

三、选择排序

原理与图示

  • 原理:对于给定的一个数组,起始从数组中找出一个最大元素放在a[n-1]处,然后再从剩下的n-1个元素中找出最大的元素放在a[n-1]处......以此类推,每次都从前面的剩余序列中找到(选择)一个最大元素放在后面
  • 例如:
    • 下面第一行是起始状态
    • 第2行执行时,从所有元素中找出最大的元素8,然后放到最后面
    • 第3行执行时,从前面剩余的所有元素中找出最大元素6,然后放到最后面(但是在8前面)
    • ......以此类推,完成排序

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第5张图片

  • 备注:此处介绍的原理是每次选择最大的元素然后放到后面,你也可以使用别的方法,例如每次从序列中选择最小的元素放到前面(原理都是类似的)
  • 因为选择排序每次就是为了从前面的剩余序列中找出最大的元素,因此选择排序的方式可能有不同种方式,下面给出了两种实现方法,它们的差异在于每次找到最大的元素时是否立即交换

代码(实现方式①

  • 首先在前面剩余的序列中找到一个最大值,然后再进行一次交换(下面两种代码都可以)
//返回数组中最大元素的索引
template
int indexOfMax(T a[], int n)
{
    if (n <= 0)
        return -1;
    int indexOfMax = 0;
    for (int i = 1; i < n; ++i)
        if (a[indexOfMax] < a[i])
            indexOfMax = i;
    return indexOfMax;
}

template
void selectionSort(T a[], int n)
{
    for (int size = n; size > 1; --size)
    {
        //获取前面剩余序列中最大元素的索引
        int j = indexOfMax(a, size);
        //然后进行交换
        swap(a[j], a[size - 1]);
    }
}
template
void selectionSort(T a[], int n)
{
    for (int i = n-1; i >=0; --i)
    {
        //首先假定索引i是最大的
        int max = i;
        //然后从前面剩余的序列中找出是否比arr[max]要大的,如果有就用max记下其索引
        for (int j = 0; j 

代码(实现方式②

  • 首先在前面剩余的序列中找到一个最大值,然后再进行一次交换
template
void selectionSort(T a[], int n)
{
    for (int i = n - 1; i > 1; --i)
    {
        //遍历前面剩余的序列
        for (int j = 0; j < i; ++j)
        {
            //主要遍历到一个比arr[i]大的就立即交换
            if (a[j] > a[i])
            {
                T temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}

测试代码如下:

int main()
{
    int a[] = { 6,5,8,4,3,1 };
    selectionSort(a, 6);
    for (const auto& elem : a)
        std::cout << elem << " ";
}

四、希尔排序

原理与图示

  • 概念:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
  • 排序步骤,以下图为例:
    • 第一次排序时,元素个数总共为12个,那么先设置增量为12/2=6,此时每间隔距离为6的一组元素进行插入排序,例如a[0]与a[6]进行比较、a[1]与a[7]进行比较......a[5]与a[11]进行比较。比较完成之后如下图第三行所示
    • 第二次排序时,设置增量为6/2=3,此时每间隔距离为3的一组元素进行插入排序,例如a[0]、a[3]、a[6]、a[9]进行比较,a[1]、a[4]、a[7]、a[10]进行比较,a[2]、a[5]、a[8]、a[11]进行比较。比较完成之后如下图第4行所示
    • 第三次排序时,设置增量为3/2=1,此时每间隔距离为1的一组元素进行插入排序,此时就是我们上面介绍的插入排序了,最终的结果如下面最后一行所示

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第6张图片

  • 该方法实质上是一种分组插入方法

代码

template
void shellSort(T a[], int n)
{
    //步长
    int step = n / 2;
    while (step > 0)
    {
        int i;
        //使用插入排序对每组数据进行排序
        for (i = step; i= 0 && a[j] > temp; j -= step)
                a[j + step] = a[j];
            a[j + step] = temp;
        }
        step = step >> 1;
    }
}

测试代码如下:

int main()
{
    int a[] = { 6,5,8,4,3,1 };
    selectionSort(a, 6);
    for (const auto& elem : a)
        std::cout << elem << " ";
}

五、归并排序

原理与图示

  • 步骤一般为:
    • 1.将序列中待排序数字分为若干组,每个数组分为一组
    • 2.将若干组两两合并,保证合并后的组是有序的
    • 3.重复第二步操作直到只剩下一组,排序完成
  • 下面是一组图解:

面试冲刺:07---常用的排序算法(冒泡、插入、选择、希尔、归并、快速)_第7张图片

代码

template
void _merge_sort(T *a, T *temp, int start, int middle, int end) {

	int i = start, j = middle + 1, k = start;

	while (i <= middle && j <= end) {
		if (a[i] > a[j]) 
			temp[k++] = a[j++];
		else
			temp[k++] = a[i++];
	}

	while (i <= middle)
		temp[k++] = a[i++];
	while (j <= end)
		temp[k++] = a[j++];

	for (i = start; i <= end; i++)
		a[i] = temp[i];
}

template
void merge_sort(T *a, T *temp, int start, int end) {
	int middle;
	if (start < end) {
		middle = start + (end - start) / 2;

		merge_sort(a, temp, start, middle);
		merge_sort(a, temp, middle + 1, end);

		_merge_sort(a, temp, start, middle, end);
	}
}

测试代码如下:

int main()
{
    int a[] = { 6,5,8,4,3,1 };
    int temp[6] = { 0 };
    merge_sort(a, temp, 0, 5);
    for (const auto& elem : temp)
        std::cout << elem << " ";
}

 

  • 在另外一篇文章中也着重介绍过归并排序(原理代码都有),可以参阅:https://blog.csdn.net/qq_41453285/article/details/104474475

六、快速排序

原理与图示

  • 基本思想:通过一趟排序将要排序的数据分割为独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

代码

  • 下面的代码中,在每次对序列进行排序时,我们选取序列最左侧的元素作为排序的基本参照点
template
void _quick_sort(T *a, int leftEnd, int rightEnd)
{
	if (leftEnd >= rightEnd)
		return;

	int leftCursor = leftEnd;
	int rightCursor = rightEnd;
	int pivot = a[leftEnd];

	while (leftCursor < rightCursor)
	{
		while (leftCursor < rightCursor && a[rightCursor] > pivot)
			rightCursor--;
		a[leftCursor] = a[rightCursor];

		while (leftCursor < rightCursor && a[leftCursor] < pivot)
			leftCursor ++ ;
		a[rightCursor] = a[leftCursor];
	}

	a[leftCursor] = pivot;

	_quick_sort(a, leftEnd, rightCursor - 1);
	_quick_sort(a, rightCursor + 1, rightEnd);
}

template
void quick_sort(T *a, int n)
{
	_quick_sort(a, 0, n - 1);
}

测试代码如下:

int main()
{
    int a[] = { 6,5,8,4,3,1 };
    quick_sort(a, 6);
    for (const auto& elem : a)
        std::cout << elem << " ";
}

 

  • 在另外一篇文章中也着重介绍过快速排序(原理代码都有),可以参阅:https://blog.csdn.net/qq_41453285/article/details/104474475

你可能感兴趣的:(面试冲刺)