排序

1.冒泡排序

  算法思想:遍历数组,比较相邻的两个元素a[i]和a[i+1],如果a[i]>a[i+1],则交换两个元素的位置(从小到大排序)。算法的时间复杂度为O(n^2), 空间复杂度为O(1),是一个稳定的算法。

  java代码实现:

    

public class BubbleSort {

    public static void sort(int[] arr) {

        // 如果数组为null或者长度为1,无序排序
        if (arr == null || arr.length == 1) {
            return;
        }

        // 外层循环控制排序的次数
        for (int i = 0; i < arr.length; i++) {
            // 设置一个标志位,表示本次排序是否发生元素交换
            boolean flag = false;
            // 内层循环表示一次排序
            for (int j = 0; j < arr.length - 1 - i; j++) {


                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;
                }
            }
            if (!flag) {
                // 如果本次排序没有发生交换,则说明数组已经有序
                break;
            }
        }
    }
}
View Code

2.插入排序

  算法思想:初始时,将第一个元素划分为有序区,后面元素为无序区,对无序区进行排序,在有序区中查找要待排元素的插入位置,将其插入。算法的时间复杂度为O(n^2),空间复杂度O(1),是一个稳定的算法。

  java代码实现:

  

public class InsertionSort {

    public static void sort(int[] arr) {

        // 省略部分判断代码

        // 外层循环控制排序的次数
        for (int i = 1; i < arr.length; i++) {
            // 当前待插入元素
            int current = arr[i];
            // 有序区的最后一个元素索引
            int j = i - 1;
            // 从后向前遍历有序区,查找待插入元素的位置
            for (; j >= 0 && arr[j] > current; j--) {
                arr[j + 1] = arr[j];
            }
            // 将待插元素插入
            arr[j + 1] = current;
        }
    }
}
View Code

3.希尔排序

  算法思想:在数组基本有序的情况下,插入排序的效率很高,为了达到这一目的,可以将数组根据一个gap进行分组,组内进行插入排序,然后逐步缩减gap,gap为1时,数组已基本有序,然后再做一次插入排序。举个栗子:数组{1, 6, 3, 8, 2, 9, 0, 4, 7, 5},首先gap为5(数组长度二分之一),分组为{1, 9},{6, 0},{3, 4},{8, 7},{2, 5},进行组间排序之后,{1, 9},{0, 6},{3, 4},{7, 8},{2, 5} -> {1, 0, 3, 7, 2, 9, 6, 4, 8, 5}然后gap缩减为2,继续分组为{1, 3, 2, 6, 8},{0, 7, 9, 4, 5},组间排序结果{1, 2, 3, 6, 8},{0, 4, 5, 7, 9}->{1, 0, 2, 4, 3, 5, 6, 7, 8, 9},最后gap缩减为1,即为插入排序,结果为{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}。希尔排序的时间复杂度取决于gap的选取,然而如何选取合适的gap是一个数学难题,希尔给出的方法是取数组长度的一半。

  java代码实现:

public class ShellSort {

    public static void sort(int[] arr) {

        int gap = arr.length / 2;
        while (gap >= 1) {
            // 分组进行插入排序
            for (int i = gap; i < arr.length; i++) {
                // 当前待插入元素
                int current = arr[i];
                // 有序区的最后一个元素索引
                int preIndex = i - gap;
                // 查找待插元素的插入位置
                for (; preIndex >= 0 && arr[preIndex] > current; preIndex = preIndex - gap) {
                    arr[preIndex + gap] = arr[preIndex];
                }
                // 将待插入元素插入
                arr[preIndex + gap] = current;
            }
            gap /=  2;
        }
    }
}
View Code

4.选择排序

  算法思想:简单粗暴,跟小孩儿选卡片排序一样,选出来最小的放第一位,再选最小的放第二位,直到排序完成。时间复杂度为O(n^2),空间复杂度为O(1),是一个不稳定的排序。选择排序的时间和数组的初始状态没有关系,就是说在最好情况下,数组原本就是有序的,和在最坏情况下,数组是逆序,选择排序的时间都差不多,因为无论怎样,都会遍历整个数组去查找最小的元素,这方面的表现倒是挺稳定的。

  java代码实现:

public class SelectionSort {

    public static void sort(int[] arr) {

        // 外层循环控制排序的次数
        for (int i = 0; i < arr.length - 1; i++) {
            // 初始设第一个元素为最小元素
            int minIndex = i;
            // 查找最小元素的索引
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
            }
            // 将最小元素交换到他最终的位置
            if (minIndex != i) {
                int temp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = temp;
            }
        }
    }
}
View Code

5. 快速排序

  算法思想:选取一个基准值pivot,将数组划分为两个部分(最复杂的部分是划分操作),一部分比pivot小,一部分比pivot大,这样就找到pivot的最终位置,递归对剩下的两部分进行快速排序。时间复杂度为O(nlogn),空间复杂度为O(1),是一个稳定的算法 。快排是第一个突破O(n^2)的排序算法。

  java代码实现:

  

public class QuickSort {

    public static void sort(int[] arr) {

        sort(arr, 0, arr.length - 1);
    }

    private static void sort(int[] arr, int left, int right) {
        // 边界条件
        if (left >= right) {
            return;
        }

        int index = partition(arr, left, right);

        // 递归
        sort(arr, left, index - 1);
        sort(arr, index + 1, right);
    }

    private static int partition(int[] arr, int left, int right) {
        // 将左边的元素选为基准值
        int pivot = arr[left];
        while (left < right) {
            while (left < right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        return left;
    }
}
View Code

6.归并排序

  算法思想:利用分治的思想,将大数组不停的分成小数组,直到划分到只含一个元素,只有一个元素的数组肯定是有序,然后将两个有序的数组进行归并,直到原数组有序。时间复杂度为O(nlogn),空间复杂度为O(n),是稳定的算法。

  java代码实现:

  

public class MergeSort {

    public static void sort(int[] arr) {

        sort(arr, 0, arr.length - 1);
    }

    private static void sort(int[] arr, int left, int right) {
        
        if (left >= right) {
            return;
        }

        int mid = (right +  left) / 2;
        // 递归
        sort(arr, left, mid);
        sort(arr, mid + 1, right);
        // 归并
        merge(arr, left, mid, right);
    }

    private static void merge(int[] arr, int left, int mid, int right) {
        // 新建一个数组,用来存放排序后的数据
        int[] temp = new int[right - left + 1];
        int i = left;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }
        while (i <= mid) {
            temp[k++] = arr[i++];
        }
        while (j <= right) {
            temp[k++] = arr[j++];
        }

        // 将排序后的数组赋给原数组区间
        for (int p = 0; p < temp.length; p++) {
            arr[left + p] = temp[p];
        }
    }
}
View Code

7.堆排序

  算法思想:利用大顶堆的根节点是最大值,将数组调整为大顶堆,然后将根元素与最后一个元素交换,再将去除最大值的数组调整为大顶堆,重复上述操作。时间复杂度为O(nlogn),空间复杂度为O(1),是一个不稳定的算法。

  java代码实现:

public class HeapSort {

    public static void sort(int[] arr) {


        // 初始化堆
        buildHeap(arr);
        // len表示无序区的长度
        int len = arr.length;
        while (len > 1) {
            swap(arr, 0, len - 1);
            len--;
            // 调整为大顶堆
            heapfy(arr, 0, len);
        }

    }

    private static void heapfy(int[] arr, int i, int len) {
        // 判断索引i是否有孩子
        int index = 2 * i + 1;
        if (index >= len) {
            return;
        }
        // 索引为i的结点至少有一个孩子
        if (index + 1 < len) {
            if (arr[index] < arr[index + 1]) {
                index++;
            }
        }
        if (arr[i] < arr[index]) {
            swap(arr, i, index);
            heapfy(arr, index, len);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static void buildHeap(int[] arr) {

        int len = arr.length;
        // 从下到上依次调整为大顶堆
        for (int i = (arr.length - 2) / 2; i >= 0; i--) {
            heapfy(arr, i, len);
        }
    }
}
View Code

 

ps:刚开始写博客,难免有疏漏的地方,而且感觉自己写的也不是很清楚,附上大佬的文章。

数据结构与算法系列By如果天空不死

posted on 2019-07-03 22:29  sweetcookie  阅读(100)  评论(0)    收藏  举报

导航