ag-常用排序算法

ag-常用排序算法

1、冒泡排序

分析:

7, 0, 6, 8, 1
相邻的两个数比较,交换,每次循环找出最大值。
第一次循环:
0, 7, 6, 8, 1
0, 6, 7, 8, 1
0, 6, 7, 1, 8

时间复杂度:O(n^2),空间复杂度:O(1)

代码:

public static void bubbleSort(Integer[] nums) {
    int len = nums.length;
    while (len > 0) {
        for (int i = 0; i < len - 1; i++) {
            int next = i + 1;
            if (nums[i] > nums[next]) {
                int temp = nums[next];
                nums[next] = nums[i];
                nums[i] = temp;
            }
        }
        len--;
    }
}

2、快速排序

分析:

8,9,1,7,2,3,5,4,6,0
↑                 ↑
8 9 1 7 2 3 5 4 6 0  基准点:8
                    <-- 
0 9 1 7 2 3 5 4 6 |0
-->
0 |9 1 7 2 3 5 4 6 9
0 6 1 7 2 3 5 4 |6 9
>>> 第一次分区可得
0 6 1 7 2 3 5 4 8 9

代码:

public static int partition(int[] arr, int left, int right) {
    // 基准点
    int pivot = arr[left];
    while (left < right) {
        // 从右往左移动,知道遇到小于pivot的元素
        while (right > left && arr[right] >= pivot) {
            right--;
        }
        arr[left] = arr[right];// 记录小于 pivot 的值
        /* 再从左往右移动,直到遇见大于 pivot 的元素 */
        while (left < right && arr[left] <= pivot) {
            left++;
        }
        arr[right] = arr[left];// 记录大于 pivot 的值
    }
    arr[left] = pivot;// 记录基准元素到当前指针指向的区域
    System.out.println(Arrays.toString(arr));
    return left;// 返回基准元素的索引
}

public static void quickSort(int[] nums, int left, int right) {
    if (left < right) {
        int index = partition(nums, left, right);
        // 基准元素左边递归
        quickSort(nums, left, index - 1);
        // 基准元素右边递归
        quickSort(nums, index + 1, right);
    }
}

3、选择排序

分析:

7, 0, 6, 8, 1
找出数组中的最小值,然后与第一位交换,然后再找出第二小的值与第二位交换
0 7 6 8 1

代码:

public static void selectSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        int minIdx = i;
        int min = arr[minIdx];

        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < min) {
                min = arr[j];
                minIdx = j;
            }
        }
        if (minIdx != i) {
            arr[minIdx] = arr[i];
            arr[i] = min;
        }
    }
}

4、堆排序

分析:

大根堆:每个节点的值都大于或者等于他的左右孩子节点的值
             12
       10          8
     6     5    4     7
   4  3   2  1   

小根堆:每个结点的值都小于或等于其左孩子和右孩子结点的值
             1
         3        4
      6     5   7    8
    9   10

两种结构映射道数组:
大根堆:12 10 8 6 5 4 7 4 3 2
小根堆:1 3 4 6 5 7 8 9 10

排序思想:
1、将待排序得数据构造成一个大根堆,此时整个数组得最大值就是堆结构得顶端。
2、将顶端的数与末尾的数交换,此时末尾的数为最大值,剩余的待排序数组个数为n-1。
3、将剩余的n-1个数构造成大根堆,再将顶端数与n-1位置的数进行交换,如此反复执行,得到有序数组。

4 6 3 5 9
1、
         4
      6     3
   5     9
对于[完全二叉树],在填满的情况下(非叶子节点都有两个子节点),每一层的元素个数是上一层的2倍,根节点的数量是1。
所以最后一层的节点数量,一定是之前所有层的节点总数+1。
可以找到第一个叶子节点,为节点总数/2,即 arr.len / 2。
第一个非叶子节点的索引就是第一个叶子结点的索引-1,即arr.len / 2 -1。
对于填不满的二叉树[非完全二叉树],这个计算方式仍然适用。
2、
           4
       |9     3
     5     |6
3、找到下一个非叶子节点4,用它和它的左右子节点进行比较,4大于3,而4小于9,交换4和9位置
           |9
       |4     3
     5     6
4、此时发现4小于5和6这两个子节点,我们需要进行调整,左右节点5和6中,6大于5且6大于父节点4,因此交换4和6的位置
           9
       |6     3
     5     4
此时就构造出来一个大根堆,下来进行排序。
如此重复上述过程。

代码:

public static void adjustHeap(int[] arr, int i, int length) {
    // 取出当前元素
    int temp = arr[i]; // 这里即第一个非叶子节点的值
    for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {// 如果左子节点小于右子节点,则k指向右子节点
        if (k + 1 < length && arr[k] < arr[k + 1]) {
            k++;
        }
        if (arr[k] > temp) {
            arr[i] = arr[k];
            i = k;
        } else {
            break;
        }
    }
    arr[i] = temp;// 将temp值放到最终的位置
}

public static void heapSort(int[] arr) {
    // 1、构建大根堆
    for (int i = arr.length / 2 - 1; i >= 0; i--) {
        adjustHeap(arr, i, arr.length);
    }
    System.out.println("------");
    // 2、调整堆结构+交换堆顶元素
    for (int j = arr.length - 1; j > 0; j--) {
        // 将堆顶的元素和末尾的元素进行交换
        int temp = arr[0];
        arr[0] = arr[j];
        arr[j] = temp;
        // 重新对堆进行调整
        adjustHeap(arr, 0, j);
    }
    System.out.println(Arrays.toString(arr));
}

5、插入排序

分析:

7, 0, 6, 8, 1
   ↑
0, 7, 6, 8, 1
0, 6, 7, 8, 1
0, 1, 6, 7, 8

代码:

public static void insertSort(int[] arr) {
    // 从第二个元素开始
    for (int i = 0; i < arr.length; i++) {
        int insertVal = arr[i];// 待插入的值
        // 待插入数前一个数的下标
        int insertIdx = i - 1;
        while (insertIdx >= 0 && insertVal < arr[insertIdx]) {
            arr[insertIdx + 1] = arr[insertIdx];
            insertIdx--;// 为了找到插入的位置吗,不断向前遍历
        }
        // 把之前存起来的要插入的数插入到对应的位置
        // insertIndex+1:顺序正确时,+1保持值不变 | 顺序不正确时(已经进了while循环减过1了):+1后就是要插入的位置
        arr[insertIdx + 1] = insertVal;
        System.out.println(Arrays.toString(arr));
    }
}

6、希尔排序

分析:

希尔排序和插入排序很相似,有点像插入排序的升级版本。
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。
|1, 4, 6, 3, |8, 9, 2, 23

代码:

// 希尔排序-插入法
public static void insertShellSort(int[] arr) {
    int count = 1;//计数
    // 分而治之,循环为每次总数除二
    for (int i = arr.length / 2; i > 0; i /= 2) {
        for (int j = i; j < arr.length; j++) {
            int index = j;
            int temp = arr[index];
            // 比较每一组的值
            if (arr[index] < arr[index - i]) {
                // 如果后面的比前面的小,则将前面的数往后移
                while (index - i > 0 && temp < arr[index - i]) {
                    arr[index] = arr[index - i];
                    index -= i;
                }
                arr[index] = temp;
            }
        }
        System.out.println("希尔排序插入法第" + (count++) + "次排序后" + Arrays.toString(arr));
    }
}

// 希尔排序交换法
public static void exchangeShellSort(int arr[]) {
    int temp;// 临时数据
    boolean flag = false;// 是否交换
    int count = 1;
    // 分而治之,将数组分组排序,i为步长
    for (int i = arr.length / 2; i > 0; i /= 2) {
        // 遍历分治的每一个分组
        for (int j = i; j < arr.length; j++) {
            // 遍历分治的每一个分组,的每一个值
            for (int k = j - i; k >= 0; k -= i) {
                if (arr[k + i] < arr[k]) {
                    // 交换
                    temp = arr[k + i];
                    arr[k + i] = arr[k];
                    arr[k] = temp;
                    flag = true;
                }
                if (!flag) {
                    break;
                } else {
                    // 为了下次判断
                    flag = false;
                }
            }
        }
        System.out.println("希尔排序交换法第" + (count++) + "次排序后" + Arrays.toString(arr));
    }
}

7、归并排序

代码:

public static int[] mergeSort(int[] sourceArray) throws Exception {
    // 拷贝数组
    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
    if (arr.length < 2) {
        return arr;
    }
    int middle = (int) Math.floor(arr.length / 2);

    int[] left = Arrays.copyOfRange(arr, 0, middle);
    int[] right = Arrays.copyOfRange(arr, middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

protected static int[] merge(int[] left, int[] right) {
    // 初始化新的数组
    int[] result = new int[left.length + right.length];
    int i = 0;
    while (left.length > 0 && right.length > 0) {
        if (left[0] <= right[0]) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        } else {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }
    }
    while (left.length > 0) {
        result[i++] = left[0];
        left = Arrays.copyOfRange(left, 1, left.length);
    }
    while (right.length > 0) {
        result[i++] = right[0];
        right = Arrays.copyOfRange(right, 1, right.length);
    }
    return result;
}

评论

暂无

添加新评论