排序算法的实现(Java)

1 篇文章 0 订阅

1. 冒泡排序

/**
 * Title: 交换排序中的冒泡排序 ,一般情形下指的是优化后的冒泡排序,最多进行n-1次比较,依赖于初始序列
 * Description:因为越大的元素会经由交换慢慢"浮"到数列的顶端(最后位置),最大的数最后才确定下来,所以称为冒泡排序
 * 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2)
 * 空间复杂度:O(1)
 * 稳 定 性:稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class BubbleSort {

    public static int[] bubbleSort(int[] target) {
        if (target != null && target.length != 1) {
            for (int i = 0; i < target.length; i++) {
                for (int j = target.length - 1; j > i; j--) {
                    if (target[j] < target[j - 1]) {
                        swap(target, j, j - 1);
                    }
                }
            }
        }
        return target;
    }

    public static int[] optimizeBubbleSort(int[] target) {
        int n = target.length;
        if (target != null && n != 1) {
            // 最多需要进行n-1躺,每一趟将比较小的元素移到前面,比较大的元素自然就逐渐沉到最后面了,这就是冒泡
            for (int i = 0; i < n - 1; i++) {
                boolean exchange = false;
                for (int j = n - 1; j > i; j--) {
                    if (target[j] < target[j - 1]) {
                        swap(target, j, j - 1);
                        exchange = true;
                    }
                }
                System.out.println(Arrays.toString(target));
                if (!exchange) {    // 若 i 到 n-1 这部分元素已经有序,则直接返回
                    return target;
                }
            }
        }
        return target;
    }

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

2. 直接插入排序

/**
 * Title: 插入排序中的直接插入排序,依赖于初始序列
 * Description: 在有序序列中不断插入新的记录以达到扩大有序区到整个数组的目的
 * 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2)
 * 空间复杂度:O(1)
 * 稳    定   性:稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */


public class StraightInsertionSort {

    public static int[] insertSort(int[] target) {
        if (target != null && target.length != 1) {
            for (int i = 0; i < target.length; i++) {
                for (int j = i; j > 0; j--) {
                    if (target[j] < target[j - 1]) {
                        int temp = target[j];
                        target[j] = target[j - 1];
                        target[j - 1] = temp;
                    }
                }
            }
        }
        return target;
    }
}

3. 直接选择排序

/**
 * Title: 选择排序中的直接选择排序,依赖于初始序列
 * Description: 每一趟 (例如第i趟,i = 0,1,...)在后面第n-i个待排序元素中选出最小元素作为有序序列的第i个元素
 * 时间复杂度:最好情形O(n^2),平均情形O(n^2),最差情形O(n^2)
 * 空间复杂度:O(1)
 * 稳    定   性:不稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class StraightSelectSort {

    public static int[] selectSort(int[] target) {
        if (target != null && target.length != 1) {
            for (int i = 0; i < target.length; i++) {
                int min_index = i;
                for (int j = i + 1; j < target.length; j++) {
                    if (target[min_index] > target[j]) {
                        min_index = j;
                    }
                }
                if (target[min_index] != target[i]) {
                    swap(target, min_index, i);
                }
            }
        }
        return target;
    }

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

4. 希尔排序

/**
 * Title: 插入排序中的希尔排序,依赖于初始序列
 * Description: 分别对间隔为gap的gap个子序列进行直接插入排序,不断缩小gap,直至为 1
 * <p>
 * 刚开始时,gap较大,每个子序列元素较少,排序速度较快;
 * 待到排序后期,gap变小,每个子序列元素较多,但大部分元素基本有序,所以排序速度仍较快。
 * <p>
 * 时间复杂度:O(n) ~ O(n^2)
 * 空间复杂度:O(1)
 * 稳    定   性:不稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class ShellSort {

    public static void shellSort(int[] target) {
        if (target != null && target.length != 1) {
            int gap = target.length;
            while (gap > 1) {
                gap = gap / 3 + 1;
                for (int i = gap; i < target.length; i++) {
                    int j = i - gap;
                    while (j >= 0) {
                        if (target[j + gap] < target[j]) {
                            swap(target, j, j + gap);
                            j -= gap;
                        } else {
                            break;
                        }
                    }
                }
            }
        }
    }

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

5. 折半插入排序

/**
 * Title: 插入排序中的折半插入排序,依赖于初始序列
 * Description: 折半搜索出插入位置,并直接插入;与直接插入搜索的区别是,后者的搜索要快于顺序搜索
 * 时间复杂度:折半插入排序比直接插入排序明显减少了关键字之间的比较次数,但是移动次数是没有改变。所以,
 * 折半插入排序和插入排序的时间复杂度相同都是O(N^2),在减少了比较次数方面它确实相当优秀,所以该算法仍然比直接插入排序好。
 * 空间复杂度:O(1)
 * 稳    定   性:稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class BinaryInsertSort {

    public static int[] binaryInsertSort(int[] target) {
        if (target != null && target.length != 1) {
            for (int i = 1; i < target.length; i++) {
                int left = 0;
                int right = i - 1;
                int mid;
                int temp = target[i];
                if (temp < target[right]) {
                    while (left <= right) {
                        mid = (left + right) / 2;
                        if (target[mid] < temp) {
                            left = mid + 1;
                        } else if (target[mid] > temp) {
                            right = mid - 1;
                        } else {
                            left = left + 1;
                        }
                    }

                    for (int j = i; j > 0; j--) {
                        target[j] = target[j - 1];
                    }
                    target[left] = temp;
                }
            }
        }
        return target;
    }
}

6. 堆排序

/**
 * Title: 堆排序(选择排序),升序排序(最大堆),依赖于初始序列
 * Description: 现将给定序列调整为最大堆,然后每次将堆顶元素与堆尾元素交换并缩小堆的范围,直到将堆缩小至1
 * 时间复杂度:O(nlgn)
 * 空间复杂度:O(1)
 * 稳 定 性:不稳定
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class HeapSort {

    public static int[] heapSort(int[] arr) {
        if (arr != null && arr.length != 1) {
            //1.构建大顶堆
            for (int i = arr.length / 2 - 1; i >= 0; i--) {
                //从第一个非叶子结点从下至上,从右至左调整结构
                sift(arr, i, arr.length);
            }
            //2.调整堆结构+交换堆顶元素与末尾元素
            for (int j = arr.length - 1; j > 0; j--) {
                swap(arr, 0, j);//将堆顶元素与末尾元素进行交换
                sift(arr, 0, j);//重新对堆进行调整
            }
        }
        return arr;
    }

    private static void sift(int[] arr, int i, int length) {
        int temp = arr[i];//先取出当前元素i
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {//从i结点的左子结点开始,也就是2i+1处开始
            if (k + 1 < length && arr[k] < arr[k + 1]) {//如果左子结点小于右子结点,k指向右子结点
                k++;
            }
            if (arr[k] > temp) {//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
        }
        arr[i] = temp;//将temp值放到最终的位置
    }

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

7. 归并排序

/**
 * Title: 归并排序 ,概念上最为简单的排序算法,是一个递归算法 Description:归并排序包括两个过程:归 和 并
 * "归"是指将原序列分成半子序列,分别对子序列进行递归排序 "并"是指将排好序的各子序列合并成原序列
 * <p>
 * 归并排序的主要问题是:需要一个与原待排序数组一样大的辅助数组空间
 * <p>
 * 归并排序不依赖于原始序列,因此其最好情形、平均情形和最差情形时间复杂度都一样 时间复杂度:最好情形O(n),平均情形O(n^2),最差情形O(n^2)
 * 空间复杂度:O(n) 稳 定 性:稳定 内部排序(在排序过程中数据元素完全在内存)
 */
public class MergeSort {

    public static void mergeSort(int[] target) {
        int[] copy = Arrays.copyOf(target, target.length);    // 空间复杂度O(n)
        mergeSort(target, copy, 0, target.length - 1);
    }

    public static void mergeSort(int[] target, int[] copy, int left, int right) {
        if (right > left) { // 递归终止条件
            int mid = (left + right) / 2;
            mergeSort(target, copy, left, mid); // 归并排序第一个子序列
            mergeSort(target, copy, mid + 1, right); // 归并排序第二个子序列
            merge(target, copy, left, mid, right); // 合并子序列成原序列
        }
    }

    /**
     * @param target 用于存储归并结果
     * @param left   第一个有序表的第一个元素所在位置
     * @param mid    第一个有序表的最后一个元素所在位置
     * @param right  第二个有序表的最后一个元素所在位置
     * @return
     * @description 两路归并算法
     */
    public static void merge(int[] target, int[] copy, int left, int mid,
                             int right) {

        // s1,s2是检查指针,index 是存放指针
        int s1 = left;
        int s2 = mid + 1;
        int index = left;

        // 两个表都未检查完,两两比较
        while (s1 <= mid && s2 <= right) {
            if (copy[s1] <= copy[s2]) { // 稳定性
                target[index++] = copy[s1++];
            } else {
                target[index++] = copy[s2++];
            }
        }

        // 若第一个表未检查完,复制
        while (s1 <= mid) {
            target[index++] = copy[s1++];
        }

        // 若第二个表未检查完,复制
        while (s2 <= right) {
            target[index++] = copy[s2++];
        }

        // 更新辅助数组 copy
        for (int i = left; i <= right; i++) {
            copy[i] = target[i];
        }
    }
}

8. 快速排序

/**
 * Title: 交换排序中的快速排序,目前应用最为广泛的排序算法,是一个递归算法,依赖于初始序列
 * Description:快速排序包括两个过程:划分 和 快排
 * "划分"是指将原序列按基准元素划分两个子序列
 * "快排"是指分别对子序列进行快排
 * <p>
 * 就平均计算时间而言,快速排序是所有内部排序方法中最好的一个
 * <p>
 * 对大规模数据排序时,快排是快的;对小规模数据排序时,快排是慢的,甚至慢于简单选择排序等简单排序方法
 * <p>
 * 快速排序依赖于原始序列,因此其时间复杂度从O(nlgn)到O(n^2)不等
 * 时间复杂度:最好情形O(nlgn),平均情形O(nlgn),最差情形O(n^2)
 * <p>
 * 递归所消耗的栈空间
 * 空间复杂度:O(lgn)
 * <p>
 * 可选任一元素作为基准元素
 * 稳 定 性:不稳定
 * <p>
 * <p>
 * 内部排序(在排序过程中数据元素完全在内存)
 */

public class QuickSort {

    public static void quickSort(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //进行第一轮排序获取分割点
        int index = partition(array, left, right);
        //排序前半部分
        quickSort(array, left, index - 1);
        //排序后半部分
        quickSort(array, index + 1, right);
    }

    /**
     * 一次快速排序
     *
     * @param array 数组
     * @param left  数组的前下标
     * @param right 数组的后下标
     * @return key的下标index,也就是分片的间隔点
     */
    public static int partition(int[] array, int left, int right) {
        /** 固定的切分方式 */
        int key = array[left];//选取了基准点
        while (left < right) {
            //从后半部分向前扫描
            while (array[right] >= key && right > left) {
                right--;
            }
            array[left] = array[right];
            //从前半部分向后扫描
            while (array[left] <= key && right > left) {
                left++;
            }
            array[right] = array[left];
        }
        array[right] = key;//最后把基准存入

        return right;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值