数据结构——八大排序算法(1)

前言

数据结构中的排序分为内排序和外排序。其中内排序是指将排序记录存放在计算机随机存储器(内存)中进行的排序过程,而外部排序因为待排序记录数量很大,以至于内存不能一次容纳全部记录,所以在排序过程中需要对外部存储器进行访问的排序过程。

在衡量两者排序的效率上,内部排序衡量的标准为时间复杂度,外部排序衡量的是读写外存的次数。

我们耳熟能详的八大排序算法就属于内排序。如下图所示:

下面我们将使用python以及C++分别来实现这些排序算法。

直接插入排序

直接插入排序的核心思想就是:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。因此该算法的时间复杂度达到了O(n^2)
因此,从上面的描述中我们可以发现,直接插入排序可以用两个循环完成:

  1. 第一层循环:遍历待比较的所有数组元素
  2. 第二层循环:将本轮选择的元素(selected)与已经排好序的元素(ordered)相比较。
    如果:selected > ordered,那么将二者交换

代码实现:

python 版本:

def insert_sort(List):
    #遍历数组中的所有元素,其中0号索引元素默认已排序,因此从1开始
    for index in range(1,len(List)):
        #将该元素与已排序好的前序数组依次比较,如果该元素小,则交换
        for each in range(0,index):
            if List[index-each-1] > List[index-each]:
                temp = List[index-each]
                List[index-each] = List[index-each-1]
                List[index-each-1] = temp
    return List;

C++ 版本:

void insert_sort(int array[], int n)
{
	int i, j;
	int temp;
	for (i = 1; i < n; i++)
	{
		for (j = i; j > 0; j--) {
			if (array[j - 1] > array[j]) {
				temp = array[j];
				array[j] = array[j - 1];
				array[j - 1] = temp;
			}
		}
	}
}

 希尔排序

希尔排序的算法思想:将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。 其复杂度也为O(n^2).

代码实现:

python版本:

def shell_sort(List):
    gap = int(len(List)/2)
    while(gap>=1):
        for each in range(0,len(List)-gap+1):
            if List[each]>List[each+gap]:
                temp = List[each+gap]
                List[each+gap] = List[each]
                List[each] = temp
        gap = int(gap/2)
    return List

C++版本:

void shell_sort(int array[], int n)
{
    int temp;
	int gap = n / 2;
	while (gap >= 1) {
		for (int i = 0; i < n - gap; i++) {
			if (array[i] > array[i + gap]) {
				temp = array[i + gap];
				array[i + gap] = array[i];
				array[i] = temp;
			}
		}
		gap = gap / 2;
	}
}

 简单选择排序

简单选择排序的基本思想:比较+交换。

代码实现:

python版本 :

  1. 从待排序序列中,找到关键字最小的元素;
  2. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
  3. 从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
    因此我们可以发现,简单选择排序也是通过两层循环实现。
    第一层循环:依次遍历序列当中的每一个元素
    第二层循环:将遍历得到的当前元素依次与余下的元素进行比较,符合最小元素的条件,则交换。
def single_select_sort(List):
    for index in range(0,len(List)):
        minNum = List[index]
        for each in range(index+1,len(List)):
            if List[each] < minNum:
                temp = List[each]
                List[each] = minNum
                minNum = temp
        List[index] = minNum
    return List

C++版本:

void singel_select_sort(int array[], int n)
{
	int temp;
	for (int i = 0; i < n; i++) {
		int minNum = array[i];
		for (int j = i + 1; j < n; j++) {
			if (array[j] < minNum) {
				temp = array[j];
				array[j] = minNum;
				minNum = temp;
			}
		}
		array[i] = minNum;
	}
}

堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构:

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤

基本思想:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

基本步骤:

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

  a.假设给定无序序列结构如下

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

b.重新调整结构,使其继续满足堆定义

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

再简单总结下堆排序的基本思路:

  a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

  b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

  c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

代码实现:

python版本

def swap(a, b):  # 将a,b交换
    temp = a
    a = b
    b = temp
    return a,b

def adjust_heap(array, start, end):
    """
    调整成大顶堆,初始堆时,从下往上;交换堆顶与堆尾后,从上往下调整
    :array: 列表的引用
    :start: 父结点
    :end: 结束的下标
    :return: 无
    """
    while True:
        # 当列表第一个是以下标0开始,结点下标为i,左孩子则为2*i+1,右孩子下标则为2*i+2;
        left_child = 2*start + 1  # 左孩子的结点下标
        if left_child > end:
            break
        # 寻找左右节点最大的节点
        if left_child+1 <= end and array[left_child+1] > array[left_child]:
            left_child += 1
        if array[left_child] > array[start]:  # 当左右孩子的最大值大于父结点时,则交换
            array[left_child], array[start] = swap(array[left_child], array[start])
            start = left_child  # 交换之后以交换子结点为根的堆可能不是大顶堆,需重新调整
        else:  # 若父结点大于左右孩子,则退出循环
            break
        #print(">>", array)


def heap_sort(array):  # 堆排序
    # 先初始化大顶堆
    first = len(array)//2 -1  # 最后一个有孩子的节点(//表示取整的意思)
    for i in range(first, -1, -1):  # 从最后一个有孩子的节点开始往上调整
        #print(array[i])
        adjust_heap(array, i, len(array)-1)  
    #print("初始化大顶堆结果:", array)
    # 交换堆顶与堆尾
    for head_end in range(len(array)-1, 0, -1):  # start stop step
        array[head_end], array[0] = swap(array[head_end], array[0]) # 交换堆顶与堆尾
        adjust_heap(array, 0, head_end-1)  # 堆长度减一(head_end-1),再从上往下调整成大顶堆

C++版本:

void swap(int &a, int &b) {
	int temp = a;
	a = b;
	b = temp;
}

void adjust_heap(int array[], int start, int end) {
	while(true) {
		int left_child = 2 * start + 1;
		if (left_child > end)
			break;
		if (left_child + 1 <= end && array[left_child + 1] > array[left_child])
			left_child += 1;
		if (array[left_child] > array[start]) {
			swap(array[left_child], array[start]);
			start = left_child;
		}
		else
			break;
	}
}

void heap_sort(int array[],int n) {
	int first = n / 2 - 1;
	for (int i = first; i >= 0; i--) 
		adjust_heap(array, i, n - 1);
	for (int j = n - 1; j >= 0; j--) {
		swap(array[j], array[0]);
		adjust_heap(array, 0, j - 1);
	}

}

小结

本篇文章主要介绍了内排序中的前四中排序算法,分别为插入排序,希尔排序,简单选择排序,以及堆排序,在下一章中,我们将继续学习剩下的四种排序算法。


ps:参考博客地址

https://blog.csdn.net/u013384984/article/details/79496052

https://www.cnblogs.com/0zcl/p/6737944.html

https://www.cnblogs.com/chengxiao/p/6129630.html

https://www.cnblogs.com/RainyBear/p/5258483.html

https://www.cnblogs.com/hokky/p/8529042.html

猜你喜欢

转载自blog.csdn.net/cv_pyer/article/details/89374275