面试笔试手撕代码 -- 排序+查找算法总结(Java+JS实现)

目录

一、排序算法

1. 冒泡排序

 2. 选择排序

3. 直接插入排序

4. 归并排序

5. 快速排序

二、查找算法

1. 线性查找

2. 二分查找

3. 斐波那契查找


一、排序算法

1. 冒泡排序

思路: 每次冒泡操作都会对相邻的两个元素进行比较,每一轮排序就找出未排序序列中最大值放在最后。

优化:第一版优化增加flag标记,没有数字交换直接return,最优时间复杂度O(n) 。第二版优化,增加tempPostion记录内循环最后一次交换的位置,来缩减内循环的次数

public static void bubbleSort(int[] array) {
    int len = array.length - 1;
    int temp; // 开辟一个临时空间, 存放交换的中间值
    int tempPostion = 0;  // 记录最后一次交换的位置
    // 要遍历的次数
    for (int i = 0; i < array.length - 1; i++) {
        int flag = 1; // 设置一个标志位
        // 依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
        for (int j = 0; j < len; j++) {
            // 比较相邻的元素,如果前面的数大于后面的数,交换
            if (array[j] > array[j + 1]) {
                temp = array[j + 1];
                array[j + 1] = array[j];
                array[j] = temp;
                flag = 0;  // 发生交换,标志位置0
                tempPostion = j;  // 记录交换的位置
            }
        }
        len = tempPostion; // 把最后一次交换的位置给len,来缩减内循环的次数
        if (flag == 1) {// 如果没有交换过元素,则已经有序
            // System.out.println(Arrays.toString(array));
            return;
        }
    }
    // System.out.println(Arrays.toString(array));
}
public static void main(String[] args) {
            int arr[] = {2, 4, 7, 6, 8, 5, 9};
            bubbleSort(arr);
            System.out.println(Arrays.toString(arr));
}
function bubbleSort(arr){
  for(var j = arr.length - 1; j >= 1; j--){
    var done = true;
      for(var i = 0; i <= j - 1; i++){
            if(arr[i] > arr[i + 1]){
              var temp = arr[i + 1];
              arr[i + 1] = arr[i];
              arr[i] = temp;
              done = false;
            }
        }
        if(done){
          break;
          }
  }
  return arr;
}

 2. 选择排序

思路:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录的位置与第一个记录的位置交换;重复该过程,直到进行比较的记录只剩下一个为止。

public static void selectSort(int[] array) {
    int len = array.length;
    for (int i = 0; i < len; i++) {//确定每次开始的位置
        int min = array[i];//设定开始数字为最小的值最小值
        int flag = i;
        for (int j = i + 1; j < len; j++) {//把最小值存放到min,从开始数字向后一个个和min比较,再把最小值存放到min
            if (min > array[j]) {
                min = array[j];
                flag = j;
            }
        }
        if (flag != i) {
            array[flag] = array[i];
            array[i] = min;
        }
    }
    // System.out.println(Arrays.toString(array));
}
public static void main(String[] args) {
        int arr[] = {2, 4, 7, 6, 8, 5, 9};
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
	}
function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for(var i = 0; i< len-1;i++){
        minIndex = i;
        for(var j = i+1;j

3. 直接插入排序

思路:在要排序的一组数中,假设前面(n-1)个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。从最后一个数开始向前循环,如果插入数小于当前数,就将当前数向后移动一位。如此反复循环,直到全部排好顺序。

public static void insertSort(int[] data) {
    int temp;
    for(int i = 1;i < data.length; i++){// 取第i个数,插入前边的有序的序列
        temp = data[i];
        int j;
        for(j = i - 1; j>=0; j--) {// 从第i-1的位置上开始比较
            if(data[j] > temp) {// 若前面的数大,则往后挪一位
                data[j+1] = data[j];
            } else {
                break;// 否则,说明要插入的数比较大
            }
        }
        data[j+1] = temp;// 找到这个位置,插入数据
    }
}
public static void main(String[] args) {
        int arr[] = {2, 4, 7, 6, 8, 5, 9};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
}
function insertSort(arr){
  for(var i = 1; i <= arr.length - 1; i++){
    var temp = arr[i];
    var j = i;
    while(j > 0 && (arr[j - 1] >= temp)){
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = temp;
  }
  return arr;
}

4. 归并排序

思路:归并排序采用经典的分治(divide-and-conquer)策略。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。治阶段,我们需要将两个已经有序的子序列合并成一个有序序列

概括:主要需要两个辅助函数:mergeSort用来递归划分区间,merge用来合并两个数组。

public static void main(String[] args) {
		int arr[] = {2, 4, 7, 6, 8, 5, 9};
        mergeSort(arr);
        System.out.println(Arrays.toString(arr));
	}
public static void mergeSort(int[] array) {
	    int[] temp = new int[array.length];// 在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
	    mergeSort(array, 0, array.length-1, temp);
	    // System.out.println(Arrays.toString(array));
	}
private static void mergeSort(int[] arr, int left, int right, int []temp) {
	    if(left < right) {
	        int mid = (left+right) / 2;
	        mergeSort(arr, left, mid, temp);// 左边归并排序,使得左子序列有序
	        mergeSort(arr, mid+1, right, temp);// 右边归并排序,使得右子序列有序
	        merge(arr, left, mid, right, temp);// 将两个有序子数组合并操作
	    }
	}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
	    int i = left;// 左序列指针
	    int j = mid+1;// 右序列指针
	    int t = 0;// 临时数组指针
	    while (i <= mid && j <= right) {
	        if(arr[i] <= arr[j]) {
	            temp[t++] = arr[i++];
	        } else {
	            temp[t++] = arr[j++];
	        }
	    }
	    while(i <= mid) {// 将左边剩余元素填充进temp中
	        temp[t++] = arr[i++];
	    }
	    while(j <= right) {// 将右序列剩余元素填充进temp中
	        temp[t++] = arr[j++];
	    }
	    t = 0;
	    // 将temp中的元素全部拷贝到原数组中
	    while(left <= right) {
	        arr[left++] = temp[t++];
	    }
	}
function mergeSort(arr) {  //采用自上而下的递归方法
    var len = arr.length;
    if(len < 2) {
        return arr;
    }
    var middle = Math.floor(len / 2),
        left = arr.slice(0, middle),
        right = arr.slice(middle);
    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    var result = [];
 
    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    while (left.length)
        result.push(left.shift());
 
    while (right.length)
        result.push(right.shift());
    return result;
}

5. 快速排序

思路:快速排序使用分治策略来把一个序列(list)分为两个子序列(sub-lists)

(1)选择主元(基准);(2)划分:左指针指向数组第一个元素,右指针指向数组最后一个元素。移动左指针直到比主元大的元素,移动右指针,直到比主元小的元素,交换他们。直到左指针超过右指针。此时主元位于中间位置(3)递归两个小数组。

public static void quickSort(int[] array) {
	    sort(array, 0, array.length - 1);
	    // System.out.println(Arrays.toString(array));
	}
private static void sort(int[] a, int low, int high) {
	    int i = low;
	    int j = high;
	    if (a.length <= 1) {
	        return;
	    }
	    if (i >= j) {
	        return;
	    }
	    int index = a[i]; // 选择第一个位置元素为主元
	    while (i < j) {
	        while (i < j && a[j] >= index)
	            j--;
	        if (a[j] < index)
	            a[i++] = a[j];
	        while (i < j && a[i] <= index)
	            i++;
	        if (a[i] > index)
	            a[j--] = a[i];
	    }
	    a[i] = index;   // 将主元放在中心位置
	    sort(a, low, i - 1);
	    sort(a, i + 1, high);
	}
public static void main(String[] args) {
        int arr[] = {2, 4, 7, 6, 8, 5, 9};
        quickSort(arr);
        System.out.println(Arrays.toString(arr));
	}
function quickSort2(arr,start,end){
    if(end - start < 1){return }
    const target = arr[start];
    let [l,r] = [start,end];
    while(l < r){
      while(l < r && arr[r]>=target){
        r--;
      }
      arr[l] = arr[r];
      while(l < r && arr[l] < target){
        l++;
      }
      arr[r] = arr[l]
    }
    arr[l] = target;
    quickSort2(arr,start,l-1);
    quickSort2(arr,l+1,end);
    return arr;
  }

二、查找算法

1. 线性查找

线性查找就是从数组的起始位置a[0]开始依次比较数组中的每一个值直到找到目标值

public class SeqSearch {
	public static void main(String[] args) {
        int[] data = {2, 1, 4, 6, 12, 7};
        int target = 12;
        int searchIndex = search(data, target);
        if (searchIndex != -1) {
            System.out.println("found at: " + searchIndex);
        }else {
            System.out.println("not found");
        }
    }
    public static int search(int[] data, int target) {
        int length = data.length;
        for (int i = 0; i < length; i++) {
            if (data[i] == target) {
                return i;
            }
        }
        return -1;
    }
}
function listSearch(arr,data) {
    var result;
    for (var i = 0;i

2. 二分查找

二分法查找适用于大的数据,但前提条件是数据必须是有序的,他的原理是先和中间的比较,如果等于就直接返回,如果小于就在前半部分继续使用二分法进行查找,如果大于则在后半部分继续使用二分法进行查找

public class BinarySearch {
	public static void main(String[] args) {
        int[] data = {1, 5, 6, 12, 15, 19, 23, 26, 30, 33, 37, 42, 53, 60};
        int target = 19;
        int index = binarySearch2(data, target);
        if (index > -1) {
            System.out.println("found :" + index);
        }else {
            System.out.println("not found");
        }
    }
// 非递归实现
public static int binarySearch2(int[] array, int value) {
	    int low = 0;
	    int high = array.length - 1;
	    while (low <= high) {
	        int middle = low + ((high - low) >> 1);
	        if (value == array[middle]) {
	            return middle;
	        }
	        if (value > array[middle]) {
	            low = middle + 1;
	        }
	        if (value < array[middle]) {
	            high = middle - 1;
	        }
	    }
	    return -1;
	}
}
// 递归实现
public static int binarySearch4(int[] array, int value) {
    int low = 0;
    int high = array.length - 1;
    return searchmy(array, low, high, value);
}
private static int searchmy(int array[], int low, int high, int value) {
    if (low > high)
        return -1;
    int mid = low + ((high - low) >> 1);
    if (value == array[mid])
        return mid;
    if (value < array[mid])
        return searchmy(array, low, mid - 1, value);
    return searchmy(array, mid + 1, high, value);
}
function binarySearch(arr,key) {
    var low = 0,
        high = arr.length-1;
    while (low <= high) {
            var mid = parseInt((low+high)/2);    
            if (key < arr[mid]) {
                high = mid-1;
            } else if (key > arr[mid]) {
                low = mid+1;
            } else {
                return mid;
            }
    }
    return -1;
}

3. 斐波那契查找

斐波那契数列我们都知道{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 },前后的比值会越来越接近0.618,也就是黄金分割点。0.618也被公认为最具有审美意义的比例数字。斐波那契查找原理其实和二分法查找原理差不多,只不过计算中间值mid的方式不同,还有一点就是斐波那契查找的数组长度必须是f(k)-1,这样我们就可以把斐波那契数列进行划分f(k)-1=f(k-1)+f(k-2)-1=(f(k-1)-1)+1+(f(k-2)-1);然后前面部分和后面部分都还可以继续进行划分。但实际上我们要查找的数组长度不可能都是f(k)-1,所以我们要补齐最后的部分,让数组的长度等于f(k)-1,让数组的最后一位数字把后面铺满。比如我们查找的数组长度是21,而f(8)-1=21-1=20;小于21,所以f(8)-1是不行的,我们需要把数组长度变为f(9)-1=34-1=33,后面多余的我们都用原数组最后的那个值填充
 

    public static int search(int[] array, int value) {
        if (array == null || array.length == 0) return -1;
        int length = array.length;
        int k = 0;
        while (length > fibonacci(k) - 1 || fibonacci(k) - 1 < 5) {
            k++;
        }
        int[] fb = makeFbArray(fibonacci(k) - 1);
        int[] temp = Arrays.copyOf(array, fb[k] - 1);
        for (int i = length; i < temp.length; i++) {
            temp[i] = array[length - 1];//用原数组最后的值填充
        }
        return fibonacciSearch(temp, fb, value, 0, length - 1, k);
    }
 
    public static int fibonacciSearch(int[] array, int[] fb, int value, int low, int hight, int k) {
        if (value < array[low] || value > array[hight] || low > hight) return -1;
        int middle = low + fb[k - 1] - 1;
        if (value < array[middle]) {
            return fibonacciSearch(array, fb, value, low, middle - 1, k - 1);
        } else if (value > array[middle]) {
            return fibonacciSearch(array, fb, value, middle + 1, hight, k - 2);
        } else {
            if (middle <= hight) {
                return middle;
            } else {
                return hight;
            }
        }
    }
 
    private static int fibonacci(int n) {
        if (n == 0 || n == 1) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
 
    public static int[] makeFbArray(int length) {
        int[] array = new int[length];
        array[0] = 0;
        array[1] = 1;
        for (int i = 2; i < length; i++) array[i] = array[i - 1] + array[i - 2];
        return array;
    }

参考链接:https://blog.csdn.net/godloveleo9527/article/details/106816989#%E4%BA%94%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F

https://juejin.im/post/5cff4b6ae51d4558936aa05a#heading-21

https://blog.csdn.net/yangchunshang/article/details/81533922?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2

Java排序算法

你可能感兴趣的:(数据结构与算法,数据结构,java,排序算法,快速排序)