一、内部排序
1、插入排序(直接插入排序、希尔排序)
/**
* 直接插入排序
* 基本操作:将一个记录插入到已排好序的有序序列中,从而得到一个新的、记录数增加1的有序序列
*/
public void insertSort(int[] nums){
int temp, j;
for (int i = 1; i < nums.length; i++) {
temp = nums[i];
j = i-1;
while (j >= 0 && temp < nums[j]){
nums[j+1] = nums[j];
j--;
}
nums[j+1] = temp;
}
}
/**
* 希尔排序(缩小增量的插入排序)
* 希尔排序时间复杂度好于O(N2),空间复杂度O(1)
*/
// 写法一
private static void shellSort(int[] arr) {
if (arr == null || arr.length == 0){
throw new NullPointerException();
}
//step:步长,也就是增量的大小,以及数组一半长度为例
for (int step = arr.length / 2; step > 0; step /= 2) {
//对一个步长区间进行比较 [step,arr.length)
for (int i = step; i < arr.length; i++) {
int value = arr[i];
int j;
//对步长区间中具体的元素进行比较
for (j = i - step; j >= 0 && arr[j] > value; j -= step) {
//j为左区间的取值,j+step为右区间与左区间的对应值。
arr[j + step] = arr[j];
}
//此时step为一个负数,[j + step]为左区间上的初始交换值
arr[j + step] = value;
}
}
}
// 写法二
// 希尔排序 针对有序序列在插入时采用交换法
public void sellSort(int[] arr){
// 定义增量gap,且逐渐缩小增量至1
for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
// 开始从索引gap时开始进行比较直至数组索引的最大值
for (int i = gap; i < arr.length; i++) {
int j = i;
while (j - gap >= 0 && arr[j] < arr[j - gap]){
swap(arr, j, j - gap);
j -= gap;
}
}
}
}
// 数组元素交换
private void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
2、选择排序(简单选择排序、堆排序)
/**
* 简单选择排序,假设元素无序,从无序序列中选择最小的元素放在无序序列第一个元素的位置
* 此时第一个元素有序,其他元素无序,以此类推;
* 空间复杂度:O(N2),空间复杂度:O(1),是一种稳定的排序算法
*/
public void selectSort(int[] nums){
int minIndex = 0;
for (int i = 0; i < nums.length; i++){
// 假设待排序区间的第一个元素为最小值的索引
minIndex = i;
// 寻找待排序区间最小值索引
for (int j = i+1; j < nums.length; j++) {
if (nums[minIndex] > nums[j]){
minIndex = j;
}
}
// 将待排序区间的第一个元素与最小值交换
swap(nums, i, minIndex);
}
}
// 数组元素交换
public static void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
//[图解](https://www.cnblogs.com/chengxiao/p/6129630.html)
/**
* 堆排序—2
* 堆是具利用完全二叉树的逻辑结构来维护的一维数组(但堆并不一定是完全二叉树)
* 大(小)顶堆:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
* 使用原因:对于随时会更新的序列,且需要随时知道序列的最小值或最大值,即使用堆排序;(数组排序插入和删除移动数组都需要O(n)的时间复杂度)
* 堆排序平均时间复杂度均为O(nlogn),是不稳定排序算法
*/
public class heapSorted {
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;
// 数组的最后一个非叶子节点的索引为:数组长度/2 - 1
int cursor = (array.length / 2) - 1;
// 先要找到最后一个非叶子节点并将该节点和叶子节点进行比较并完成堆排序,循环结束就可以第一次排序完成
for (int i = cursor; i >= 0; i--) {
maxHeap(array, array.length, i);
//minHeap(array, array.length, i);
}
}
// 大顶堆
public static void maxHeap(int[] array, int heapSize, int index) {
int left = index * 2 + 1; // 左子节点
int right = index * 2 + 2; // 右子节点
int maxValueIndex = index; // 暂时定在Index的位置就是最大值
// 如果左子节点的值,比当前最大的值大,就把最大值的位置换成左子节点的位置
if (left < heapSize && array[left] > array[maxValueIndex]) {
maxValueIndex = left;
}
// 如果右子节点的值,比当前最大的值大,就把最大值的位置换成右子节点的位置
if (right < heapSize && array[right] > array[maxValueIndex]) {
maxValueIndex = right;
}
// 如果不相等,说明啊,这个子节点的值有比自己大的,位置发生了交换了位置
if (maxValueIndex != index) {
swap(array, index, maxValueIndex); // 就要交换位置元素
// 交换完位置后还需要判断子节点是否打破了最大堆的性质。最大堆性质:两个子节点都比父节点小。
maxHeap(array, heapSize, maxValueIndex);
}
}
// 小顶堆
public static void minHeap(int[] array, int heapSize, int index) {
int left = index * 2 + 1; // 左子节点
int right = index * 2 + 2; // 右子节点
int minValueIndex = index; // 暂时定在Index的位置就是最小值
// 如果左子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
if (left < heapSize && array[left] < array[minValueIndex]) {
minValueIndex = left;
}
// 如果右子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
if (right < heapSize && array[right] < array[minValueIndex]) {
minValueIndex = right;
}
// 如果不相等,说明啊,这个子节点的值有比自己小的,位置发生了交换了位置
if (minValueIndex != index) {
swap(array, index, minValueIndex); // 就要交换位置元素
// 交换完位置后还需要判断子节点是否打破了最小堆的性质。最小性质:两个子节点都比父节点大。
minHeap(array, heapSize, minValueIndex);
}
}
// 数组元素交换
public static void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
}
3、交换排序(冒泡排序、快速排序)
/**
* 冒泡排序
* 冒泡排序是一种交换排序算法,通过比较每一对相邻的元素进行排序
* 比如:第一个比第二个大,就交换元素,直到将最大的元素交换到数组最后,然后再比较[0,n-1]区间的元素,以此类推
* 通过n-1次循环将元素排序,每次都将最大元素交换到待排序区间的最后
* 时间复杂度:O(n2),是一种稳定的排序算法
*/
public void bubbleSort(int[] nums){
int flag = 0;
int temp = 0;
for (int i = nums.length -1; i >= 1; --i){
/** 变量flag用来标记本次排序是否交换了元素 */
flag = 0;
for (int j = 1; j <=i; ++j){
if (nums[j-1] > nums[j]){
swap(nums, j, j - 1);
/** 如果本次循环发生交换则赋值1 */
flag = 1;
}
}
/** 若某次排序过程中未发生交换,则证明该序列有序,排序结束 */
if (flag == 0){
break;
}
}
}
// 数组元素交换
public static void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
/**
* 快速排序
* 通过每一趟排序将待排序的数组分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再递归对这两部分数据进行排序
* 比如:将数组中元素依次和索引为0的元素进行比较,将小于该元素的索引元素放在该元素左边,将大于该元素的放在该元素右边完成一次排序,然后依次对这两部分进行递归
* 时间复杂度:O(lgN),空间复杂度:O(1),是一种不稳定的排序算法
*/
public void quickSort1(int[] nums, int left, int right){
if (nums == null || nums.length == 0){
throw new NullPointerException("");
}
int i = left, j = right;
if (left < right){
int temp = nums[left];
/** 以nums[left]为参照,将比其小的放在该元素左边,大的放在右边 */
while (i < j){
while (i < j && nums[j] >= temp) {
j--;
}
if (i < j){
nums[i] = nums[j];
i++;
}
while (i < j && nums[i] < temp) {
i++;
}
if (i < j){
nums[j] = nums[i];
j--;
}
}
/** 将temp放在最终位置上 */
nums[i] = temp;
/** 递归对temp左右的元素进行排序 */
quickSorted(nums, left, i-1);
quickSorted(nums, i+1, right);
}
}
4、基数排序
class lSRSort{
/**
* 基数排序-1:Least Significant Digital
* @param nums
* @param numsLength
*/
public void radixSort(int[] nums,int numsLength){
int max = nums[0];
for(int i = 1 ;i < numsLength;i++){
if(max < nums[i])
max = nums[i];
}
double d = Math.pow(10, String.valueOf(max).length());
int k = 1;
int[][] bucket = new int[10][numsLength]; //桶 bucket 用于存储符合条件的元素,也就是作为位数位为0-9的数的桶
int[] count = new int[numsLength]; //count 记录每个桶中存入数的个数
while(k < d){
for(int a : nums){
int m = (a / k) % 10;
bucket[m][count[m]] = a;
count[m]++;
}
/**
* 将按照某一位排序好的有序序列重新串起来放入原数组中
*/
int c = 0;
for(int i = 0; i < numsLength; i++){
if(count[i] != 0){
for(int j = 0;j < count[i];j++){
nums[c++] = bucket[i][j];
}
}
count[i] = 0;
}
k = k * 10;
}
}
public static void main1(String[] args) {
lSRSort r = new lSRSort();
int[] A = {12, 1, 23, 11, 5444, 0, 5, 123, 34};
r.radixSort(A, A.length);
for(int a : A){
System.out.println(a);
}
}
}
/**
* 基数排序-2:Most Significant Digital
*/
class MSDSort {
public int[] mSDSort(int[] A, int n){
int max = A[0];
for(int i = 1 ;i < n;i++){
if(max < A[i])
max = A[i];
}
int maxL = String.valueOf(max).length(); //获取数组中最长元素长度
int k = new Double(Math.pow(10, maxL - 1)).intValue();
int[][] t = new int[10][n]; //桶
int[] num = new int[n]; //记录每个桶中存入数的个数
for(int a : A){ //按最高位入桶
int m = (a / k) % 10;
t[m][num[m]] = a;
num[m]++;
}
int c = 0;
for(int i = 0; i < n; i++){
if(num[i] == 1){ //如果桶中只有一个数则直接取出
A[c++] = t[i][0];
}else if(num[i] > 1){ //如果桶中不止一个数,则另存如数组B递归
int[] B = new int[num[i]];
for(int j = 0;j < num[i];j++){
B[j] = t[i][j];
mSDSort(B,num[i]); //递归方法
}
}
}
return A;
}
public static void main(String[] args) {
MSDSort r = new MSDSort();
int[] A = {12,1,23};
r.mSDSort(A, A.length);
for(int a : A){
System.out.println(a);
}
}
}
5、归并排序
二、外部排序