算法初步 —— 排序算法&查找(冒泡+选择+快排+折半)

一、排序

排序是算法学习过程中入门必学的模块之一,虽然在实际编程时很少有程序员动手写排序算法(C++库中封装的sort函数是底层是快排实现的)。下面我将介绍最常见的三种排序算法:(1)冒泡排序 (2)选择排序 (3)快速排序,其中冒泡排序和选择排序

1. 蛮力法

(1)冒泡排序

冒泡排序,顾名思义,排序过程就像冒泡一样,每次将最大的数冒到最上面并固定,然后依次将剩余的元素的最大值再冒到最上面,如下图所示:
在这里插入图片描述

算法:BubbleSort(A[0...n-1])
// 该算法用冒泡排序对数组A[0...n-1]排序
// 输入:一个可排序的数组A
// 输出:非降序排列的数组
for i ⬅ 0 to n-2 do
		for j ⬅ 0 to n-2-i do
				if A[j + 1] < A[j]
						swap A[j] and A[j + 1]

例如下面的例子,第一次循环时,我们目标是将整个数组的最大值置于队尾
在这里插入图片描述
下面来解读一下上面的伪代码:一共有两重循环,第一重循环是冒泡的次数,要想对整个数组进行排序,只需要对n - 1个数字进行排序(即找出前n - 1大的数,剩下一个位置和一个数字,不需要排序),而每次排序的目的是将待排序数字中的最大值冒到数组最末尾,第一次循环(i=0)只需要 j 从0到n-2(倒数第二个位置), 分别比较A[j]和A[j+1],如果前者更大,则交换二者,最终将最大值冒到最后。第二次循环(i=1),则 j 从0到n - 3 ······ 因此,第二重循环为j从0 ~ j - 2 - i。因为冒泡排序是两重规模为n的循环,因此时间复杂度为O(n^2)

(2)选择排序

在这里插入图片描述

选择排序和冒泡排序的思想很相似,也是一次循环处理将一个元素归位,每次将待排序数组中最小值置于首位。和冒泡排序的区别是:冒泡排序是不停的进行两两交换,而选择排序每次循环中不交换,而是使用一个变量来记录目前遍历到的元素的最小值的位置,当遍历完整个数组后一次性进行交换。伪代码如下:

算法:SelectionSort(A[0...n-1])
// 该算法用选择排序对给定的数组排序
// 输入:一个可排序数组A[0...n-1]
// 输出:非降序排列的数组A[0...n-1]
for i ⬅ 0 to n-2 do
		min ⬅ i
		for j ⬅ i+1 to n-1 do
				if A[j] < A[min]
						min ⬅ j
		swap A[i] and A[min]

当然,选择排序的核心还是二重循环,因此时间复杂度仍然是O(n^2)

2. 分治算法(快速排序)

快速排序算法是目前公认的平均效率最快的算法,和其他算法相比,快排的特点是比较次数和交换次数都很少,平均时间复杂度只有O(nlogn),下面一起来看一下是如何实现的吧:
在这里插入图片描述
(1)选定一个数字作为基准值pivotKey,这里方便起见,规定为最右侧元素6
(2)两个标记符(指针)low,high分别指向首元素A[0]和倒数第二个元素A[n-2]
(3)将low右移,只要满足A[low] < pivotKey ,就继续右移,直到不满足时,停止右移,考虑high指针;同理,将high左移,只要满足A[high] > pivotKey,就继续左移,直到不满足。
(4)直到两边都不满足时,交换二者。(左标记low是为了找到大于pivotKey的数字,右标记是为了找到小于pivotKey的数字,通过交换,可以在左侧收集到小于pivotKey的数组,右侧收集到大于pivotKey的数字)
(5)只要low < high,就不断执行上面步骤,当low >= high时,其实跳出循环时low == high,此时指针所指向的位置就是pivotKey元素排序后应该在的位置k
(6)分别对k左边的子数组和右边的子数组执行上述步骤,最终得到排序好的数组。

int Partition(SqList &L, int low, int high)  {
    
    
    KeyType pivotkey;
    pivotkey = L.r[low].key;
    while (low<high) {
    
    
         while ((low<high)&&(L.r[high].key>=pivotkey))
               --high;
          L.r[low] ←→ L.r[high];
          while ((low<high)&&(L.r[low].key<=pivotkey))
               ++low;
           L.r[low] ←→ L.r[high];
   }
   return low;                                   // 返回枢轴位置
} // Partition

QUICKSORT(s,t,A)
LIST  F;`
int s,t;
{
    
    
    int i;
    if (s<t) {
    
    
         i=PARTITION(s,t,A);
        QUICKSORT(s,i-1,A);
        QUICKSORT(i+1,t,A);
   }
}

二、折半查找

折半查找的核心在于折半,也就是只需要进行一次比较就可以删除一半的元素。要想实现这样的效果,就必须保证数组是有序的,只需要比较待查找元素和中间位置元素的大小关系即可。如果待查找元素比中间元素小,则该元素肯定在左边一半中出现(或者不出现),同理,如果待查找元素比中间元素大,则该元素肯定在右边一半中出现(或者不出现)。

算法 BinarySearch(A[0..n-1],K)
//非递归折半查找
//输入:升序数组A[0..n-1]和查找键K
//输出:找到键K,返回K所在下标,否
则返回-1
l ⬅ 0; r ⬅ n-1;
while (l ≤ r) do{
    
    
     m ⬅ (l + r) / 2;
     if (K = A[m]) return m;
     else if (K<A[m]) r ⬅ m-1;
          else l ⬅ m+1;
}
return -1;

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_51339444/article/details/124219392
今日推荐