Java——排序(冒泡排序,选择排序、插入排序,快速排序,堆排序)

各排序算法的时间复杂度、空间复杂度及稳定性
在这里插入图片描述

冒泡排序

排序思想

就像“冒泡泡”一样,从头开始,两个两个数作比较,将两个数中较大(较小)的数向后移,再比较下面两个数,依次将较大(较小)的数向后移,最终,将一组数中最大(最小)的数,移动到最后面,再从头开始,移下一个数
下面是一个栗子:
要排序数组(升序):{4,2,7,3,1}
————————————
第一趟(交换四次):

  • 第一次:4与2比较,4大于2,交换两个数——{2,4,7,3,1}
  • 第二次:4与7比较,7大于4,不交换位置——{2,4,7,3,1}
  • 第三次:7与3比较,7大于3,交换两个数——{2,4,3,7,1}
  • 第四次:7与1比较,7大于1,交换两个数——{2,4,3,1,7}

第二趟(交换3次):

  • 第一次:2与4比较,4大于2,不交换位置——{2,4,3,1,7}
  • 第二次:4与3比较,4大于3,交换两个数——{2,3,4,1,7}
  • 第三次:4与1比较,4大于1,交换两个数——{2,3,1,4,7}

第三趟(交换两次):

  • 第一次:2与3比较,3大于2,不交换位置——{2,3,1,4,7}
  • 第二次:3与1比较,3大于1,交换两个数——{2,1,3,4,7}

第四趟(交换一次):

  • 第一次:2与1比较,2大于1,交换两个数——{1,2,3,4,7}

————————————
根据上面的栗子可以得出有n个数字要进行排序总共要进行 n - 1 趟,每 i 趟要排序 n - i - 1 次

排序优点

每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,每进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。

时间复杂度

冒泡排序的时间复杂度为O(N^2)

空间复杂度

冒泡排序的空间复杂度为O(1)

实现代码
/**
 * 冒泡排序(升序)
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 16:10
 */
public class BubbleSort {
    public static void bubbleSort(int[] arr)
    {
        if(arr == null || arr.length == 0)
        {
            return;
        }
        for(int i = 0;i < arr.length - 1;i++)
        {
            for(int j = 0;j < arr.length - 1 - i;j++)
            {
                if(arr[j] > arr[j + 1])
                {
                    swap(arr,j,j + 1);
                }
            }
        }
    }

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

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        bubbleSort(arr);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}

选择排序

排序思想

每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。也就是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。
下面是一个栗子:
要排序数组(升序):{4,2,7,3,1}
————————————
第一趟:

  • 将数组中最小的元素1与第一个元素4交换——{1,2,7,3,4}

第二趟:

  • 将除了第一个元素外的最小元素2,与第二个位置的元素2交换(此处就是2,因此不用交换)——{1,2,7,3,4}

第三趟:

  • 将除了前两个元素外的最小元素3与第三个位置的元素7交换——{1,2,3,7,4}

第四趟:

  • 将除了前三个元素外的最小元素4与第四个位置的元素7交换——{1,2,3,4,7}

————————————

时间复杂度

选择排序的时间复杂度为O(N^2)

空间复杂度

选择排序的空间复杂度为O(1)

代码实现
package selectSort;

/**
 * 选择排序(升序)
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 16:31
 */
public class SelectSort {
    public static void selectSort(int[] arr)
    {
        if(arr == null || arr.length == 0)
        {
            return;
        }
        for(int i = 0;i < arr.length - 1;i++)
        {
            int index = i;
            for(int j = i + 1;j < arr.length;j++)
            {
                if(arr[j] < arr[index])
                {
                    swap(arr,j,index);
                }
            }
        }
    }
    private static void swap(int[] arr, int j, int i) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        selectSort(arr);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}


插入排序

排序思想

插入排序就像是玩扑克牌时要把牌放到手中以有的牌中一样,像下面这张图一样,原本手中的牌是{2,4,5,10},但是抽到了一张7,此时应该怎么办?
将牌从右向左看,7比10小,比5大,那么正好就放到这里,那么为什么不继续向前面比较了?是因为牌本身是有顺序的

我盗图了
图片是盗的
下面是一个情况最坏的栗子:
要排序数组(升序):{5,4,3,2}
————————————
第一次插入:

  • 将位置1(数组从位置0开始算起,并且默认位置0,即第一个元素已经插入完成)的元素4开始与它之前的元素5进行比较,发现4比5小,将4和5交换{4,5,3,2}

第二次插入:

  • 将位置2的元素3与它之前的元素5进行比较,发现3比5小,将3和5交换{4,3,5,2}
  • 将位置1的元素3与它之前的元素4进行比较,发现3比4小,将3和4交换{3,4,5,2}

第三次插入:

  • 将位置3的元素2与它之前的元素5进行比较,发现2比5小,将2和5交换{3,4,2,5}
  • 将位置2的元素2与它之前的元素4进行比较,发现2比4小,将3和5交换{3,2,4,5}
  • 将位置1的元素2与它之前的元素3进行比较,发现2比3小,将2和3交换{2,3,4,5}

————————————

时间复杂度

插入排序的时间复杂度为O(N^2)

空间复杂度

插入排序的空间复杂度为O(1)

代码实现
package insertSort;

/**
 * 插入排序(升序)
 * 该种方法需要不断地进行交换赋值,这样会产生时间地浪费
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 16:48
 */
public class InsertSort {
    public static void insertSort(int[] arr)
    {
        if(arr == null || arr.length == 0)
        {
            return;
        }
        //默认第一个位置的元素摆放完毕,从第二个位置的元素开始
        for(int i = 1;i < arr.length;i++)
        {
            for(int j = i;j > 0 && arr[j] < arr[j - 1];j--)
            {
                swap(arr,j,j - 1);
            }
        }
    }
    private static void swap(int[] arr, int j, int i) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        insertSort(arr);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}

package insertSort;

/**
 * 插入排序(升序)
 * 该种方法是上面那种方法地改进版本,不需要来回地交换元素
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 16:48
 */
public class InsertSort2 {
    public static void insertSort(int[] arr)
    {
        if(arr == null || arr.length == 0)
        {
            return;
        }
        //默认第一个位置的元素摆放完毕,从第二个位置的元素开始
        for(int i = 1;i < arr.length;i++)
        {
            int j;
            int tmp = arr[i];
            for(j = i;j > 0 && arr[j - 1] > tmp;j--)
            {
                arr[j] = arr[j - 1];
            }
            arr[j] = tmp;
        }
    }
    private static void swap(int[] arr, int j, int i) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        insertSort(arr);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}


快速排序

排序思想

以一个值为基准(通常以第一个值),然后将比他小的数移到它左边,比他大的数移到右边,那么问题就转化为要找到中间分隔的基准值
下面是一个栗子:
要排序数组(升序):{6,1,2,7,9,3,4,5,10,8}
————————————
使用两个指针start和end分别指向开头位置和结尾位置,将开始第一个位置的元素设为
寻找中间分隔点:
(1)只要end指针指向的值比keyValue大,就将end指针一次向前移动,最后end指针所指向的值为从后往前第一个小于keyValue的值,此时将end位置上的值赋给start位置,见下图

这里写图片描述
(2)然后从start位置开始,只要start位置上的值小于keyValue的值,就将start位置向前移,最终start停留的位置为从前往后第一个大于keyValue的位置,此时将此时的start位置的值赋给end位置,见下图
这里写图片描述
(3)然后再次将end按照(1)的规则将end指针向前移,并将end位置的值赋给start位置,
将start按照(2)的规则将start指针向后移,并将start位置的值赋给end
见下图
这里写图片描述
(4)将end按照(1)的规则向前移并赋值,将start按照(2)的规则向后移,发现两个指针重合,见下图
这里写图片描述

(5)此时将keyValue的值赋给start位置,见下图
这里写图片描述
此时,在start位置的左边的数全部比该值小,start位置右边的数全部比该值大,然后以该位置为分界点key将数组变为两个部分,再分别进行这样的操作,最终完成排序

详细见博客:http://developer.51cto.com/art/201403/430986.htm

时间复杂度
  • 快速排序的时间复杂度平均为O(NlogN)
    在最好情况下,每次划分所取的基准都是当前无序区的"中值"记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(N
    logN)

  • 快速排序的时间复杂度最坏为O(N^2)
    最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做nN1次划分,第i次划分开始时区间长度为N-i+1,所需的比较次数为N-i(1≤i≤N-1),故总的比较次数达到最大值:Cmax= N*(N-1)/2=O(N^2)

空间复杂度
  • 快速排序的空间复杂度为O(logN)
    快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为O(logN),故递归后需栈空间为O(logN)
代码实现
递归实现
package quickSort;

/**
 * 快速排序(递归实现)
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 17:28
 */
public class QuickSort {
    public static void quickSort(int[] arr,int start,int end)
    {
        if(arr == null || arr.length == 0 || start > end)
        {
            return;
        }
        int key = quickSortHelp(arr,start,end);
        quickSort(arr,start,key - 1);
        quickSort(arr,key + 1,end);
    }

    //找中间节点位置的辅助函数
    private static int quickSortHelp(int[] arr, int start, int end) {
        if(arr == null || arr.length == 0 || start > end)
        {
            return -1;
        }
        int keyValue = arr[start];
        while (start < end)
        {
            while (start < end && arr[end] > keyValue)
            {
                //找到第一个小于keyValue的元素
                end--;
            }
                //交换两个元素
                arr[start] = arr[end];
            while (start < end && arr[start] <= keyValue)
            {
                //找到第一个大于keyValue的元素
                start++;
            }
                arr[end] = arr[start];
        }
        arr[start] = keyValue;
        return start;
    }

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        quickSort(arr,0,arr.length - 1);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}

非递归实现
package quickSort;

import java.util.Stack;

/**
 * 快速排序(非递归实现)
 * @author LXY
 * @email 403824215@qq.com
 * @date 2018/8/18 17:28
 */
public class QuickSortRec {
    public static void quickSort(int[] arr,int start,int end)
    {
        if(arr == null || arr.length == 0 || start > end)
        {
            return;
        }
        Stack<Integer> stack = new Stack<>();
        if(start < end)
        {
            stack.push(end);
            stack.push(start);
            while (!stack.isEmpty())
            {
                int left = stack.pop();
                int right = stack.pop();
                int key = quickSortHelp(arr,left,right);
                if(left < key)
                {
                    //出栈元素在标记位置左边
                    //将标记位置和刚才出栈的元素依次入栈
                    stack.push(key - 1);
                    stack.push(left);
                }
                if(right > key)
                {
                    //出栈元素在标记位置右边
                    //将刚才出栈的元素和标记位置的元素依次入栈
                    stack.push(right);
                    stack.push(key + 1);
                }
            }
        }
    }

    //找中间节点位置的辅助函数
    private static int quickSortHelp(int[] arr, int start, int end) {
        if(arr == null || arr.length == 0 || start > end)
        {
            return -1;
        }
        int keyValue = arr[start];
        while (start < end)
        {
            while (start < end && arr[end] > keyValue)
            {
                //找到第一个小于keyValue的元素
                end--;
            }
                //交换两个元素
                arr[start] = arr[end];
            while (start < end && arr[start] <= keyValue)
            {
                //找到第一个大于keyValue的元素
                start++;
            }
                arr[end] = arr[start];
        }
        arr[start] = keyValue;
        return start;
    }

    public static void main(String[] args) {
        int[] arr = {4,2,7,3,1,8,9,5};
        quickSort(arr,0,arr.length - 1);
        for(int i:arr)
        {
            System.out.print(i + " ");
        }
    }
}

堆排序

参考自:http://blog.51cto.com/shangdc/2073589

代码实现
public class HeapSortFinal {

    public static void main(String[] args) {
        int[] array = { 19, 38, 7, 36, 5, 5, 3, 2, 1, 0, 56 };

        System.out.println("排序前:");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + ",");
        }

        System.out.println();
        System.out.println("分割线---------------");

        heapSort(array);

        System.out.println("排序后:");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + ",");
        }
    }

    //降序
    public static void heapSort(int[] array) {
        if (array == null || array.length == 1)
            return;

        buildMaxHeap(array); // 第一次排序,构建最大堆,只保证了堆顶元素是数组里最大的

        for (int i = array.length - 1; i >= 1; i--) {
            // 这个是什么意思呢?,经过上面的一些列操作,目前array[0]是当前数组里最大的元素,需要和末尾的元素交换
            // 然后,拿出最大的元素
            swap(array, 0, i);

            // 交换完后,下次遍历的时候,就应该跳过最后一个元素,也就是最大的那个值,然后开始重新构建最大堆
            // 堆的大小就减去1,然后从0的位置开始最大堆
            maxHeap(array, i, 0);
            //minHeap(array, i, 0);
        }
    }

    // 构建堆
    public static void buildMaxHeap(int[] array) {
        if (array == null || array.length == 1)
            return;

        // 堆的公式就是 int root = 2*i, int left = 2*i+1, int right = 2*i+2;
        int cursor = array.length / 2;
        for (int i = cursor; i >= 0; i--) { // 这样for循环下,就可以第一次排序完成
            maxHeap(array, array.length, i);
            //minHeap(array, array.length, i);
        }
    }

    // 最大堆
    public static void maxHeap(int[] array, int heapSieze, int index) {
        int left = index * 2 + 1; // 左子节点
        int right = index * 2 + 2; // 右子节点
        int maxValue = index; // 暂时定在Index的位置就是最大值

        // 如果左子节点的值,比当前最大的值大,就把最大值的位置换成左子节点的位置
        if (left < heapSieze && array[left] > array[maxValue]) {
            maxValue = left;
        }

        // 如果右子节点的值,比当前最大的值大,就把最大值的位置换成右子节点的位置
        if (right < heapSieze && array[right] > array[maxValue]) {
            maxValue = right;
        }

        // 如果不相等,说明啊,这个子节点的值有比自己大的,位置发生了交换了位置
        if (maxValue != index) {
            swap(array, index, maxValue); // 就要交换位置元素

            // 交换完位置后还需要判断子节点是否打破了最大堆的性质。最大堆性质:两个子节点都比父节点小。
            maxHeap(array, heapSieze, maxValue);
        }
    }

    // 最小堆
    public static void minHeap(int[] array, int heapSieze, int index) {
        int left = index * 2 + 1; // 左子节点
        int right = index * 2 + 2; // 右子节点
        int maxValue = index; // 暂时定在Index的位置就是最小值

        // 如果左子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
        if (left < heapSieze && array[left] < array[maxValue]) {
            maxValue = left;
        }

        //  如果右子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
        if (right < heapSieze && array[right] < array[maxValue]) {
            maxValue = right;
        }

        // 如果不相等,说明啊,这个子节点的值有比自己小的,位置发生了交换了位置
        if (maxValue != index) {
            swap(array, index, maxValue); // 就要交换位置元素

            // 交换完位置后还需要判断子节点是否打破了最小堆的性质。最小性质:两个子节点都比父节点大。
            minHeap(array, heapSieze, maxValue);
        }
    }

    // 数组元素交换
    public static void swap(int[] array, int index1, int index2) {
        int temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }

}

归并排序

参考自:https://blog.csdn.net/qq_21150865/article/details/61918216

代码实现
public class MyMergrSort {

    private static void sort(int[] array, int start, int end) {
        if (start >= end)
            return;

        int mid = (start + end) >> 1;
        // 递归实现归并排序
        sort(array, start, mid);
        sort(array, mid + 1, end);
        mergerSort(array, start, mid, end);
    }

    // 将两个有序序列归并为一个有序序列(二路归并)
    private static void mergerSort(int[] array, int start, int mid, int end) {
        // TODO Auto-generated method stub
        int[] arr = new int[end + 1]; // 定义一个临时数组,用来存储排序后的结果
        int low = start; // 临时数组的索引
        int left = start;

        int center = mid + 1;
        // 取出最小值放入临时数组中
        while (start <= mid && center <= end) {
            arr[low++] = array[start] > array[center] ? array[center++] : array[start++];
        }

        // 若还有段序列不为空,则将其加入临时数组末尾

        while (start <= mid) {
            arr[low++] = array[start++];
        }
        while (center <= end) {
            arr[low++] = array[center++];
        }

        // 将临时数组中的值copy到原数组中
        for (int i = left; i <= end; i++) {
            array[i] = arr[i];
        }
    }

    public static void main(String[] args) {
        int[] array = { 58, 48, 69, 87, 49, 59, 25, 35, 68, 48 };
        sort(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值