基本排序算法Java实现归纳(一)

对于程序员来说,数据结构算是一门基础,而数据结构中,排序算法又是面试中最为经典的考题。感觉到自己的数据结构基础不太牢固,所以对数据结构进行了温习,今天对排序算法这一章节进行归纳,使用Java语言进行编程。主要介绍以下几种排序算法:

  1. 插入排序
    1.1 直接插入排序
    1.2 折半插入排序
    1.3 希尔排序
  2. 交换排序
    2.1 冒泡排序
    2.2 快速排序
  3. 选择排序
    3.1 简单选择排序
    3.2 堆排序
  4. 归并排序

1. 插入排序

1.1 直接插入排序

直接插入排序的算法思想是:设待排序的数据在数组r[0…n]中,从第二个数开始(即r[1])循环n-1次,每次都将该数字r[i]与它前一个数字r[index-1]进行比较。若r[i]大于r[i-1],则本次循环不做任何操作,继续下一循环;若r[i]小于r[i-1],则从下标i-1开始对数组r从后往前进行循环遍历,若r[i]小于当前遍历的数,则将当前遍历的数后移,若r[i]大于当前遍历的数或已经遍历完数组,则停止循环,将r[i]插入正确位置。简单来说直接插入排序就是不断的将待排序的数据提出来,然后在已排序的数据中按顺序遍历找到正确的位置进行插入。以下是直接插入排序的java实现:

    /**
     * 
     * 直接插入排序
     * 时间复杂度O(n²)
     * 空间复杂度O(1)
     * 稳定排序
     * 适合基本有序情况
     * 
     */
    private static void directInsertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            if(array[i] < array[i-1]) {
                int temp = array[i];
                int j;
                for (j = i-1; (j>=0)&&(temp < array[j]); j--) {
                    array[j+1] = array[j];
                }

                array[j+1] = temp;
            }
        }
        printArray(array);
    }

    private static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
    }

1.2 折半插入排序

折半插入排序的算法思想与直接插入排序的算法思想类似:设待排序的数据在数组r[0…n]中,从第二个数开始(即r[1])循环n-1次,每次都将该数字r[i]与它前一个数字r[index-1]进行比较。若r[i]大于r[i-1],则本次循环不做任何操作,继续下一循环;与直接插入排序不同之处在于,若r[i]小于r[i-1],则初始化变量low=0,high=i-1,将r[i]与r[mid]进行比较,用于判断r[i]是在r[mid]的左边区间还是右边区间,按照这样的思路重复取中值,直到low与high相等,则说明这是正确的插入位置,此时将正确位置及其以后的所有记录后移,将r[i]插入正确的位置,完成排序。就平均性能而言,折半插入排序优于直接插入排序,通过取中值减少了关键字比较的次数,但当待排序序列为基本有序时,直接插入排序优于折半插入排序。以下是折半插入排序的java实现:

    /**
     * 
     * 折半插入排序
     * 时间复杂度O(n²)
     * 空间复杂度O(1)
     * 稳定排序
     * 适合初始记录无序、n较大的情况
     * 
     */
    private static void binaryInsertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int temp = array[i];
            int low = 0;
            int high = array.length-1;

            while (low <= high) {
                int m = (low + high) / 2;
                if(temp < array[m]) {
                    high = m - 1;
                }
                else {
                    low = m + 1;
                }
            }

        int j;
        for (j = i-1; j >= high+1;) {
            array[j+1] = array[j];
            j--;
            if (j<0) {
                break;
            }
        }
        array[j+1] = temp;

        }

        printArray(array);
    }

1.3 希尔排序

希尔排序又称“缩小增量排序”,由于直接插入排序当待排的记录个数少且基本有序的时候,效率较高。所以希尔排序基于以上两点,从“减少记录个数”和“序列基本有序”两个方面对直接插入排序进行了改进。希尔排序设置了一个增量数组,根据增量对全部记录进行分组,在组内进行直接插入排序。增量数组的具体数据没有要求,但是最后的增量必须为1,当增量大于1的时候,关键字是跳跃式移动,而不是一步一步移动,所以使得在进行最后一趟增量为1的插入排序中,序列已基本有序。以下是希尔排序的java实现:

    /**
     * 
     * 希尔排序
     * 时间复杂度O(n的1.3次方)
     * 空间复杂度O(1)
     * 记录跳跃式移动导致不稳定排序
     * 适合初始记录无序、n较大的情况
     * 
     */
    private static void ShellSort(int[] array) {
        //定义增量数组
        int dt[] = {5, 3, 1};
        for (int i = 0; i < dt.length; i++) {
            ShellInsert(array, dt[i]);
        }
        printArray(array);
    }

    private static void ShellInsert(int[] array, int t) {
        for (int i = t; i < array.length; i++) {
            if(array[i] < array[i-t]) {
                int tmp = array[i];
                int j;

                for (j = i-t; (j>=0)&&(tmp < array[j]); j -= t) {
                    array[j+t] = array[j];
                }

                array[j+t] = tmp;
            }
        }
    }

2. 交换排序

2.1 冒泡排序

冒泡排序是一种最简单的交换排序方法,它通过两两比较相邻记录交换逆序,使得关键字大的记录往后坠落,每经过一趟排序,都能够得到一个序列最大值。其算法思想是:设待排序的数据在数组r[0…n]中,从第一个记录与第二个记录开始,两两比较大小消除逆序,以此类推,直至第n-1个记录与第n个记录比较过为止,经过一趟排序,r[n]所存放的记录为序列最大值;然后进行第二趟排序,对前n-1个记录进行相同操作,经过这趟排序,r[n-1]所存放的记录为序列r[0…n-1]的最大值,重复上述过程,直到某一趟排序过程中没有进行过交换记录的操作,说明完成排序。以下是冒泡排序的java实现:

    /**
     * 
     * 冒泡排序
     * 时间复杂度O(n²)
     * 空间复杂度O(1)
     * 可用于链式存储结构
     * 稳定排序
     * 平均时间性能比直接插入排序差
     * 
     */
    /**
     * 
     * 冒泡排序
     * 时间复杂度O(n²)
     * 空间复杂度O(1)
     * 可用于链式存储结构
     * 稳定排序
     * 平均时间性能比直接插入排序差
     * 
     */
    private static void bubbleSort(int[] array) {
        int flag = 1;
        for (int i = array.length - 1; i > 0 && flag == 1; i--) {
            flag = 0;
            for (int j = 0; j < i; j++) {
                if(array[j] > array[j+1]) {
                    flag = 1;
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
        printArray(array);
    }

2.2 快速排序

快速排序是由冒泡排序改进而得的,快速排序的本质是递归算法。在冒泡排序中,只对相邻两个记录进行比较,每次交换两个相邻记录只能消除一个逆序,快速排序通过两个不相邻记录的一次交换,可以消除多个逆序,提高了算法的效率。快速排序的基本思想是:对于一趟快速排序来说,从序列中任意选择一个记录作为key,并将序列的上下界设置为low和high,从high侧开始向左遍历,high指向遍历记录(high–),如果找到一个数字小于key,则将这个数字赋给low所指记录,然后从low侧开始向右遍历,low指向遍历记录(low++),如果找到一个记录大于key,则将这个数字赋给high所指记录,重复这两个步骤,直到low和high相等为止,将key赋给low=high所指记录。经过一趟快速排序,将所有小于key的值放在了key的左边,大于key的值放到了key的右边。然后再在序列0到key下标和key+1到序列length之间继续重复上述操作,最后使得序列有序。以下是快速排序的java实现:

    /**
     * 
     * 快速排序
     * 时间复杂度O(nlog2n)
     * 空间复杂度最好O(log2n)最差O(n)
     * 需要定位表的上界和下界适合顺序结构
     * 不稳定排序
     * 平均情况是所有内部排序方法中速度最快的一种
     * 
     */
    private static void quickSort(int[] array) {
        qSort(array, 0, array.length-1);
        printArray(array);
    }

    private static void qSort(int[] array, int low, int high) {
        if(low < high) {
            int index = Partition(array, low, high);
            qSort(array, low, index-1);
            qSort(array, index+1, high);
        }
    }

    private static int Partition(int[] array, int low, int high) {
        int tmp = array[low];
        while (low < high) {
            while (low<high && array[high]>=tmp) {
                high--;
            }
            array[low] = array[high];   

            while (low<high && array[low]<=tmp) {
                low++;
            }
            array[high] = array[low];
        }
        array[low] = tmp;
        return low;
    }

以上是对插入排序和交换排序两类基本排序算法思路的简单解释以及算法实现,选择排序和归并排序排序算法可以参考此链接:基本排序算法Java实现归纳(二)

猜你喜欢

转载自blog.csdn.net/qq_34842671/article/details/68944472