vlambda博客
学习文章列表

算法之希尔排序、快速排序、二分查找


1


希尔排序:其时间复杂度为O(nlog n)

原理:希尔排序是插入排序的升级版本。插入排序的思想是(以从小到大排序为例):假设某个元素是最小的,然后在数列中找出该元素需要插入的位置。插入排序在数列基本有序的时候比较高效。那么,希尔排序比插入排序多哪个步骤呢?其实,希尔排序就是将插入排序跳跃的间距变成一个从大递减的数,那么一层一层的使数组接近有序,为插入排序提供一个好的条件。因此,希尔排序实现代码基本思想和插入排序一致,只是间距有所改变。我们来看希尔排序的实现代码:


Java实现:


import java.util.Arrays;
/** * @Author junkle * @Date 2020/4/26 - 22:19 * @Version 1.0 **/public class shellSort { public static void shell_Sort(int[] array){ if (array == null){ throw new RuntimeException("array is null"); } int size = array.length; int insertVal = 0; int insertIndex = 0; int jump = size >> 1; while (jump != 0) { for (int i = jump; i < size; i++) { insertVal = array[i]; insertIndex = i - jump; while (insertIndex >= 0 && insertVal < array[insertIndex]) { array[insertIndex + jump] = array[insertIndex]; insertIndex-=jump; } if (insertIndex != i - jump) { array[insertIndex + jump] = insertVal; } } jump = jump >> 1; } }
public static void main(String[] args) { int[] array = new int[]{7,6,8,2,0,12,3}; shell_Sort(array); System.out.println(Arrays.toString(array)); }}




C++实现:


#include <iostream>#include <cassert>using namespace std;

/*1、先判断传进来的参数是不是合适2、然后再进行别的操作*/void shellSort(int* array, int size){ //这个用来对两个变量进行限制 assert(array != nullptr && size != 0,"array is prohibit null"); int insertVal = 0; int insertIndex = 0; int jump = size >> 1; while (jump) { for (int i = jump; i < size; i++) { insertVal = array[i]; insertIndex = i - jump; while (insertIndex >= 0 && insertVal < array[insertIndex]) { array[insertIndex + jump] = array[insertIndex]; insertIndex-=jump; } if (insertIndex != i - jump) { array[insertIndex + jump] = insertVal; } } jump = jump >> 1; }}

int main(){ int* array = new int[5]{9,1,2,3,4}; shellSort(array, 5); for (int i = 0; i < 5; i++) { cout << array[i] << " "; } cout << endl; return 0;}


2



快速排序(冒泡排序的升级版):其时间复杂度为O(nlog n)

快速排序原理分析:先选出一个基准元素(pivot),将数组变为基准元素左边的元素都比基准元素小,基准元素右边的元素都比基准元素大。我们下面将基准元素选择为数组最左边的元素。


为了容易理解:我们可以认为是将基准元素进行挖空的操作,然后找到该位置适合填入的元素,从最右边开始往左找找出一个小于基准元素。然后将该元素填入到基准元素的位置,现在基准元素空出来,多了一个相同元素的较大值,那么我们再从左往右进行查找,看是否能找到一个大于基准元素的值来填补到空缺的那个位置上。这样一个循环,循环结束条件是左边的索引大于右边的索引了。一次这样进行递归,遇到结束条件的时候,终止操作。


下图是经历过一次转移之后的结果,快速排序是分治思想的一种体现。

参考博客https://blog.csdn.net/zxd8080666/article/details/97395765




下面我们来看具体代码实现:


Java实现:


public static void quicklySort(int[] array, int left, int right){ int i = left, j = right, x = array[left]; while (i < j){ while (i < j && array[j] > x) {j--; } if (i < j) { array[i++] = array[j]; } while (i < j && array[i] < x) { i++; } if (i < j) { array[j--] = array[i]; }; } array[j] = x; if (left < i) quicklySort(array,left,i - 1); if (j < right) quicklySort(array,j + 1,right);
}




C++实现:


void quicklySort(int* array, int left, int right){ int i = left, j = right, x = array[left]; while (i < j) { while (i < j && array[j] > x) j--; if (i < j) array[i++] = array[j]; while (i < j && array[i] < x) i++; if (i < j) array[j--] = array[i]; } array[i] = x; if (left < i) { quicklySort(array, left, i - 1); } if (j < right) { quicklySort(array, j + 1, right); }}


3



查找算法有线性查找(比较简单)、二分查找(递归思想)、斐波那契查找(二分查找的升级版)

二分查找:需要注意的是,二分查找必须保证数组是连续的。就像我们找自己期末考试的卷子一样,假设试卷是按学号进行排列的,需要找到学号为20的试卷。在找到 30 号的试卷时,我们绝对不会再向40的方向找。这就是二分查找法的经典体现。

我们很容易想到了用递归进行实现,那么,我们需要查找的条件是什么呢?

只有当传入的左边索引大于右边索引的时候,停止查找。注意:相等的时候有可能就是要找到的对象。


Java实现:


package FindAlgorithm;
import java.util.ArrayList;
public class TwoDivideFind { public static ArrayList<Integer> find(int[] array, int low, int high, int findValue) { if (low > high){ return new ArrayList<Integer>(); } int l = low, h = high, mid = ((l + h) >> 1), x = array[mid]; if (findValue < x){ return find(array, low, mid - 1, findValue); }else if(findValue > x){ return find(array, mid + 1, high, findValue); }else{// return mid; ArrayList<Integer> integers = new ArrayList<>(); integers.add(mid); int tmp = mid - 1; while (tmp >= low && array[tmp] == findValue){ integers.add(tmp); tmp--; } tmp = mid + 1; while (tmp <= high && array[tmp] == findValue){ integers.add(tmp); tmp++; } return integers; } } public static void main(String[] args) { //如果此时想要查找一个连续出现的数字的个数的 int[] array = new int[]{1,1,1,1,1,1,1,2,3,4,5,6,20}; System.out.println(find(array, 0, 6, 1)); }}


C++实现:


int twoDivideFind(int* array, int left, int right, int findVal){ if (left > right) return -1; int mid = ((left + right) >> 1); int tmp = array[mid]; if (findVal > tmp) { twoDivideFind(array, mid + 1, right,findVal); }else if (findVal < tmp) { twoDivideFind(array, left, mid - 1, findVal); } else { return mid;  }}