算法之希尔排序、快速排序、二分查找
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++实现:
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;
}
}