排序算法简介

排序总览

       排序大的分类可以分为两种:内排序和外排序。在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序。下面讲的排序都是属于内排序。内排序有可以分为以下几类:

      (1)、插入排序:直接插入排序、希尔排序
      (2)、选择排序:选择排序、堆排序
      (3)、交换排序:冒泡排序、快速排序

排序相关定义

  • 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的;否则称为不稳定的。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

排序复杂度

       

排序算法

       插入排序
              基本思想

              插入排序是一种非常简单的排序方法,它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数加1的有序表。 
           

              算法演示

                    
            代码实现

def insertSort(arr):
    n = len(arr)
    for i in range(1, n):
        if arr[i] < arr[i-1]:
            cur = arr[i]
            j = i - 1
            while j >= 0 and arr[j] > cur:
                arr[j+1] = arr[j]
                j -= 1
            arr[j+1] = cur


      希尔排序
              基本思想

              先将整个带排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序

             
              算法演示

               
           

              代码实现

def shellSort(arr):
    n = len(arr)
    gap = 1
    while gap < n/3:
        gap = gap*3 + 1
    while gap >= 1:
        for i in range(gap, n):
            if arr[i] < arr[i-gap]:
                j = i
                cur = arr[i]
                while j >= gap and arr[j-gap] > cur:
                    arr[j] = arr[j-gap]
                    j -= gap
                arr[j] = cur
        gap = gap//3


    
       冒泡排序

              基本思想

              从头依次比较相邻两条记录的关键字,如果是逆序的,则交换,这样,一趟比较的结果就是,最大的记录被安置在最后。然后以此类推,就会得到一个有序的列表


              代码实现

def bubbleSort(arr):
    n = len(arr)
    for i in range(0, n-1):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j],arr[j+1] = arr[j+1],arr[j]

       快速排序
              基本思想

              通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序


              算法演示

                 
              代码实现

def quickSort(arr):
    qSort(arr, 0, len(arr)-1)

def qSort(arr, low, high):
    if low < high:
        pivotLoc = partition(arr, low, high)
        qSort(arr, low, pivotLoc-1)
        qSort(arr, pivotLoc+1, high)

def partition(arr, low, high):
    pivotIndex = low
    pivot = arr[pivotIndex]
    while low < high:
        while low < high and arr[high] >= pivot:
            high -= 1
        arr[low] = arr[high]
        while low < high and arr[low] <= pivot:
            low += 1
        arr[high] = arr[low]
    arr[low] = pivot
    return low

       选择排序
              基本思想

             先在未排序序列中找到最小记录,存放到排序序列的起始位置,然后,再从剩余未排序记录中继续寻找最小元素,然后放到已排序序列的末尾;以此类推,直到所有记录均排序完毕


              代码实现

def selectSort(arr):
    n = len(arr)
    for i in range(0, n-1):
        minIndex = i
        for j in range(i+1, n):
            if arr[j] < arr[minIndex]:
                minIndex = j
        if minIndex != i:
            arr[minIndex], arr[i] = arr[i],arr[minIndex]


       堆排序

              完全二叉树的性质:

                     如果n个节点的完全二叉树的节点按照层次并按从左到右的顺序从0开始编号,对于任意一个结点都有:

                     1). 序号为0的节点是根,对于i>0,其父节点的编号为(i-1)/2
                     2). 若2*i+1<n,其左子节点的序号为2*i+1,否则没有左子节点
                     3). 若2*i+2<n,其右子节点的序号为2*i+2,否则没有右子节点
    
              堆定义:

                     n个元素的序列{k0, k1, k2, k3, ..., kn-1}, 当且仅当满足如下关系时,称之为堆
                     ki <= k2i+1 and ki <= k2i+2 (小顶堆)
                     或  
                     ki >= k2i+1 and ki >= k2i+2 (大顶堆)
                    堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左右孩子结点的值

              堆排序:

              若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序

              算法演示:

      

              代码实现:

def heapAdjust(arr, start, end):
    i = start*2+1
    while i <= end:
        if i < end and arr[i] < arr[i+1]:
            i += 1
        if arr[start] > arr[i]:
            break
        arr[start],arr[i] = arr[i],arr[start]
        start = i
        i = i*2+1

def heapSort(arr):
    n = len(arr) - 1
    for i in  range(n/2, -1, -1):
        heapAdjust(arr, i, n)
    for i in range(n, 0, -1):
        arr[0],arr[i] = arr[i],arr[0]
        heapAdjust(arr, 0, i-1)

其他

       php 的 sort

PHP_FUNCTION(sort)
{
	zval *array;
	zend_long sort_type = PHP_SORT_REGULAR;
	compare_func_t cmp;

	ZEND_PARSE_PARAMETERS_START(1, 2)
		Z_PARAM_ARRAY_EX(array, 0, 1)
		Z_PARAM_OPTIONAL
		Z_PARAM_LONG(sort_type)
	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

	cmp = php_get_data_compare_func(sort_type, 0);

	if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) {
		RETURN_FALSE;
	}
	RETURN_TRUE;
}

ZEND_API int ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, compare_func_t compar, zend_bool renumber)
{
	Bucket *p;
	uint32_t i, j;

	IS_CONSISTENT(ht);
	HT_ASSERT_RC1(ht);

	if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { /* Doesn't require sorting */
		return SUCCESS;
	}

	if (HT_IS_WITHOUT_HOLES(ht)) {
		i = ht->nNumUsed;
	} else {
		for (j = 0, i = 0; j < ht->nNumUsed; j++) {
			p = ht->arData + j;
			if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
			if (i != j) {
				ht->arData[i] = *p;
			}
			i++;
		}
	}

	sort((void *)ht->arData, i, sizeof(Bucket), compar,
			(swap_func_t)(renumber? zend_hash_bucket_renum_swap :
				((HT_FLAGS(ht) & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));

    //此处省略100行
}

ZEND_API void zend_sort(void *base, size_t nmemb, size_t siz, compare_func_t cmp,swap_func_t swp)
{
	while (1) {
		if (nmemb <= 16) {
			zend_insert_sort(base, nmemb, siz, cmp, swp);
			return;
		} else {
			char *i, *j;
			char *start = (char *)base;
			char *end = start + (nmemb * siz);
			size_t offset = (nmemb >> Z_L(1));
			char *pivot = start + (offset * siz);

			if ((nmemb >> Z_L(10))) {
				size_t delta = (offset >> Z_L(1)) * siz;
				zend_sort_5(start, start + delta, pivot, pivot + delta, end - siz, cmp, swp);
			} else {
				zend_sort_3(start, pivot, end - siz, cmp, swp);
			}
			swp(start + siz, pivot);
			pivot = start + siz;
			i = pivot + siz;
			j = end - siz;
			while (1) {
				while (cmp(pivot, i) > 0) {
					i += siz;
					if (UNEXPECTED(i == j)) {
						goto done;
					}
				}
				j -= siz;
				if (UNEXPECTED(j == i)) {
					goto done;
				}
				while (cmp(j, pivot) > 0) {
					j -= siz;
					if (UNEXPECTED(j == i)) {
						goto done;
					}
				}
				swp(i, j);
				i += siz;
				if (UNEXPECTED(i == j)) {
					goto done;
				}
			}
done:
			swp(pivot, i - siz);
			if ((i - siz) - start < end - i) {
				zend_sort(start, (i - start)/siz - 1, siz, cmp, swp);
				base = i;
				nmemb = (end - i)/siz;
			} else {
				zend_sort(i, (end - i)/siz, siz, cmp, swp);
				nmemb = (i - start)/siz - 1;
			}
		}
	}
}

问题:

       已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法对数组进行排序。

猜你喜欢

转载自blog.csdn.net/weixin_42521745/article/details/86026857