package com.whw.sortPractice;
import java.util.Arrays;
public class Sort
{
/**
* 遍历一个数组
* @param sortArray
*/
public void outputArray(int[] sortArray)
{
for(int i = 0;i < sortArray.length;i++)
{
System.out.print(sortArray[i] + " ");
}
System.out.println();
}
// **********************************插入排序**********************************
/**
* 插入排序
* 排序的基本思想是把待排元素前面的元素当做是一个有序列表 把待排元素后面的元素当做一个无序列表
* 只要把待排元素插入到前面的有序列表中某个位置即可
* 待排元素从数组中的第二个数开始到数组中的最后一个数结束
*
* 插入排序的效率非常低 因为一次只能将数据移动一位
* @param sortArray
*/
public void insertSort(int[] sortArray)
{
// 这里的i记录的是对数组中的第几个元素进行排序
for(int i = 1;i < sortArray.length;i++)
{
// 把待排元素放到临时变量里面
int tem = sortArray[i];
// j保存的是待排元素最后去的位置的前一个位置
int j = 0;
for(j = i - 1;(j >= 0) && (sortArray[j] > tem);j--)
{
sortArray[j+1] = sortArray[j];
}
sortArray[j+1] = tem;
}
// 输出结果
outputArray(sortArray);
}
/**
* shell排序是快速排序的改良版
* shell排序先将要排序的序列切割成多个组 然后在每个组内再使用插入排序
* @param sortArray 待排序的数组
*/
public void shellSort(int[] sortArray)
{
// 用这个数组里面的数去依次切割待排序的数组
int[] increArray = new int[]{5,4,3,2};
// 使用增量数组里面的增量依次排序
for(int i = 0;i < increArray.length;i++)
{
// 使用每一个增量进行插入排序 循环一开始 j就定位到了子数组中的第二个元素
for(int j = i + increArray[i];j < sortArray.length;j += increArray[i])
{
// 保存待排序元素的临时变量
int tem = sortArray[j];
int k = j - increArray[i];
for(k = j - increArray[i];(k >= 0) && (sortArray[k] > tem);k -= increArray[i])
{
sortArray[k + increArray[i]] = sortArray[k];
}
k += increArray[i];
sortArray[k] = tem;
}
System.out.println("第" + (i+1) + "趟排序完成 开始遍历数组元素");
outputArray(sortArray);
}
System.out.println("shell排序完成,开始插入排序");
insertSort(sortArray);
}
// *************************************选择排序*******************************
/**
* 选择排序的基本思想是 每次都选择出从第i到最后一个元素中最小(大)的那一个与第i个元素交换位置
* @param sortArray
*/
public void selectSort(int[] sortArray)
{
// 外层循环n-1次
for(int i = 0;i < sortArray.length - 1;i++)
{
// 找出第i个到最后的元素中最小的那个元素
// 最小的元素
int min = sortArray[i];
// 最小元素出现的位置
int pos = i;
for(int j = i;j < sortArray.length;j++)
{
if(sortArray[j] < min)
{
min = sortArray[j];
pos = j;
}
}
if(i != pos)
{
// 把最小元素和第i个元素交换位置
sortArray[pos] = sortArray[i];
sortArray[i] = min;
}
}
}
/**
* 二元选择排序 每趟排序中选出最大值和最小值 然后分别把这个最大值和最小值放到参与排序的元素的最左端和最右端
* 总共循环[n/2](下取整)次
* @param sortArray
*/
public void twoEleSelectSort(int[] sortArray)
{
// 循环次数
int cycleTime = sortArray.length / 2;
for(int i = 0;i < cycleTime;i++)
{
// 记录最大最小数以及他们出现的位置
int min = sortArray[i];
int minPos = i;
int max = sortArray[i];
int maxPos = i;
// 找出待排元素的最大值和最小值
for(int j = i;j < sortArray.length - i;j++)
{
if(sortArray[j] < min)
{
min = sortArray[j];
minPos = j;
}
if(sortArray[j] > max)
{
max = sortArray[j];
maxPos = j;
}
}
// 把最大值和最小值分别放到待排元素的最左端和最右端
sortArray[minPos] = sortArray[i];
sortArray[i] = min;
sortArray[maxPos] = sortArray[sortArray.length - i - 1];
sortArray[sortArray.length - i - 1] = max;
}
}
/**
* 堆排序 可以把一个数组逻辑化成一棵二叉堆 父节点的值大于任一子节点的值时是大根堆 父节点的值小于任一子节点的值是小根堆
* 大根堆的根节点就是当前数组中最大的元素 然后把这个最大的元素和待排元素中最后一个元素交换位置即可
* 排序的过程就是 建树->交换 这个过程循环往复
* 相当于每次通过建树的方式找出待排元素中最大值的那个元素
* @param sortArray
*/
/**
* @param sortArray
*/
public void heapSort(int[] sortArray)
{
// 建树 父节点和子节点元素数组下标之间的关系为 父节点的下标是i 左子节点的下标是2*i+1 右子节点的下标是2*i+2
// 建树的基本思路是 从最后一个父节点开始保证每个父节点要比左右孩子节点的值大
// 所以要找到最后一个父节点然后不停的和左右孩子节点进行比较和交换
// 通过这样不停的比较和交换最终能保证 sortArray[0]一定是数组元素中的最大值
// 最外层的循环指定了要对哪些元素进行建树
int arrLength = sortArray.length;
for(int i = arrLength - 1; i >= 0;i--)
{
// 建树 从给定元素的最后一个开始
for(int j = i; j >= 0;j--)
{
// 判断左子节点是否存在
if(2 * j + 1 >= arrLength || 2 * j + 1 > i)
{
continue;
}
// 如果比左子节点要小就要和左子节点交换
if(sortArray[j] < sortArray[2*j+1])
{
int tem = 0;
tem = sortArray[j];
sortArray[j] = sortArray[2*j+1];
sortArray[2*j+1] = tem;
}
// 判断右子节点是否存在
if(2 * j + 2 >= arrLength || 2 * j + 2 > i)
{
continue;
}
// 如果比右子节点要小就要和右子节点交换
if(sortArray[j] < sortArray[2*j+2])
{
int tem = 0;
tem = sortArray[j];
sortArray[j] = sortArray[2*j+2];
sortArray[2*j+2] = tem;
}
}
// 建树完成 这个时候的sortArray[0]就是待排元素中值最大的那个
int tem = 0;
tem = sortArray[0];
sortArray[0] = sortArray[i];
sortArray[i] = tem;
outputArray(sortArray);
}
}
// ****************************************交换排序****************************
/**
* 冒泡排序 每次都从数组中的第一个元素开始 每次都把待排元素中的最大的那个沉到最后一个
* 每次比较相邻的两个元素 如果后面的比前面的小就交换
* @param sortArray
*/
public void bubbleSort(int[] sortArray)
{
// 外层循环控制待排元素的终点
int endPostion = sortArray.length - 1;
for(int i = endPostion;i >= 0;i--)
{
// 挨个比较待排元素中相邻的元素
for(int j = 0;j < i;j++)
{
if(sortArray[j] > sortArray[j+1])
{
int tem = 0;
tem = sortArray[j];
sortArray[j] = sortArray[j+1];
sortArray[j+1] = tem;
}
}
}
}
/**
* 冒泡算法改进版
* 在每一趟排序中增加一个boolean类型的变量 标识这一趟排序中是否有交换发生 如果没有交换发生说明数组已经是有序的
* @param sortArray
*/
public void bubbleSortAdvanced(int[] sortArray)
{
// 外层循环控制待排元素的终点
int endPosition = sortArray.length - 1;
boolean isChange = true;
for(int i = endPosition;i >= 0;i--)
{
if(isChange == false)
{
return;
}
isChange = false;
for(int j = 0;j < i;j++)
{
if(sortArray[j] > sortArray[j+1])
{
int tem = sortArray[j];
sortArray[j] = sortArray[j+1];
sortArray[j+1] = tem;
// 修改交换标识符
isChange = true;
}
}
}
}
/**
* 双元素冒泡排序算法 每一趟排序中都把最大的元素沉到待排元素的最右端 把最小元素沉到待排元素的最左端
* @param sortArray
*/
public void twoElementBubbleSort(int[] sortArray)
{
// 外层循环控制待排元素的终点
int endPosition = sortArray.length - 1;
boolean isChange = true;
for(int i = endPosition;i >= 0;i--)
{
if(isChange == false)
{
return;
}
isChange = false;
// 把最大的元素沉下去
for(int j = 0;j < i;j++)
{
if(sortArray[j] > sortArray[j+1])
{
int tem = sortArray[j];
sortArray[j] = sortArray[j+1];
sortArray[j+1] = tem;
// 修改交换标识符
isChange = true;
}
}
// 把最小的元素浮上来
for(int j = i;j > 0;j--)
{
if(sortArray[j] < sortArray[j-1])
{
int tem = sortArray[j - 1];
sortArray[j - 1] = sortArray[j];
sortArray[j] = tem;
isChange = true;
}
}
}
}
/**
* 快速排序 快速排序的规则如下
* 1、设置两个变量i、j初始化的时候i= j=sortArray.length-1 也就是说这两个指针初始化的时候一个指向第一个待排元素 一个指向最后一个待排元素
* 2、每次把待排元素的第一个当做关键数据赋值给key 即key=sortArray[0]
* 3、j向前移动 找到第一个比key小的元素sortArray[j] 让sortArray[j]和sortArray[i]交换
* 4、i向后移动 找到第一个比key大的元素sortArray[i] 让sortArray[i]和sortArray[j]交换
* 5、重复3到4 直到i和j相等
* 注意:快速排序中需要注意的地方有两个
* 1、只有sortArray[i]小于等于key的时候i才向前移动 或 sortArray[j]大于等于key的时候j才向后移动
* 2、根据注意的第一点 如果sortArray[i]和sortArray[j]交换 i j 这两个指针都不移动
*
* 经过一趟排序之后数组就变成了key左边的元素都不大于key key右边的元素都不小于key
* 只要对key左右两侧的子数组进行递归排序即可
* @param sortArray
*/
public void quickSort(int[] sortArray,int startIndex,int endIndex)
{
// 递归终止条件
if(startIndex >= endIndex)
{
return;
}
// 第一趟排序
int i = startIndex;
int j = endIndex;
int key = sortArray[i];
// 当两个指针没有相遇的时候循环要一直持续
// 这个循环结束的时候i和j已经相等
outer:while(i < j)
{
System.out.println(key);
// sortArray[j]比key要大指针就一直向前移动
while(sortArray[j] >= key)
{
System.out.println(sortArray[j] + " " + key);
j--;
// 这里也是一个需要注意的地方 如果j和i已经相等了就要结束外层循环
if(i == j)
{
break outer;
}
}
if(i < j)
// sortArray[j]小于key的时候交换sortArray[i]和sortArray[j]
System.out.println("右侧交换" + sortArray[i] + " " + sortArray[j]);
int tem = sortArray[i];
sortArray[i] = sortArray[j];
sortArray[j] = tem;
outputArray(sortArray);
System.out.println("右侧交换完成");
// sortArray[i]比key要小指针就一直向后移动
while(sortArray[i] <= key && j > i)
{
i++;
if(i == j)
{
break outer;
}
}
// sortArray[i]大于key的时候交换sortArray[i]和sortArray[j]
System.out.println("左侧交换" + sortArray[i] + " " + sortArray[j]);
tem = sortArray[i];
sortArray[i] = sortArray[j];
sortArray[j] = tem;
outputArray(sortArray);
System.out.println("左侧交换完成");
}
outputArray(sortArray);
System.out.println("一次排序结束");
// 递归key左边的部分
quickSort(sortArray, startIndex, i-1);
// 递归key右边的部分
quickSort(sortArray, i+1,endIndex);
}
/**
* 归并排序
* 归并排序的思路不算复杂 主要是递归 合并
* 先把待排数组从中间分开然后对左右两个子数组进行递归 直到子数组被划分为只有两个元素 对这两个元素进行递归之后然后合并 在这个合并的过程中两个元素发生了交换
* 首先要理解递归的过程 其次要理解合并
* 现在对这个算法还不是很理解
* @param sortArray
*/
public void mergeSort(int[] sortArray,int low,int high)
{
// 这一步就是在不停的拆分数组
int mid = (low + high) / 2;
if(low < high)
{
// 通过左右两侧的递归就能保证左右两侧的子数组全部有序
// 递归执行左边的部分
mergeSort(sortArray, low, mid);
// 递归执行右边的部分
mergeSort(sortArray, mid + 1, high);
System.out.println("左右递归完成 测试左右数组是否有序");
for(int i = low;i <= mid;i++)
{
System.out.print(sortArray[i] + " ");
}
System.out.print("--------");
for(int i = mid+1;i <= high;i++)
{
System.out.print(sortArray[i] + " ");
}
System.out.println();
// 左右数组已经有序 可以进行交换
// 先把sortArray中的元素放到tem中 然后把tem中的元素赋值给sortArray中
// 这里的数组长度开辟出错 调了很长时间才调出来
int[] tem = new int[high - low + 1];
// 分别指向左右两侧数组第一个元素的指针
int leftIndex = low;
int rightIndex = mid + 1;
// 标识tem的指针
int temIndex = 0;
while(leftIndex <= mid && rightIndex <= high)
{
if(sortArray[leftIndex] < sortArray[rightIndex])
{
tem[temIndex] = sortArray[leftIndex];
leftIndex++;
}
else
{
tem[temIndex] = sortArray[rightIndex];
rightIndex++;
}
temIndex++;
}
System.out.println("全部移出");
System.out.println("tem[]中的元素为" + Arrays.toString(tem));
outputArray(sortArray);
while(leftIndex <= mid)
{
tem[temIndex] = sortArray[leftIndex];
leftIndex++;
temIndex++;
}
while(rightIndex <= high)
{
tem[temIndex] = sortArray[rightIndex];
rightIndex++;
temIndex++;
}
for(int i = 0;i < tem.length;i++)
{
sortArray[low+i] = tem[i];
}
// 把tem中的前temIndex-1个移动到sortArray中 从low开始向后temIndex-1个位置上面去
/*int temIndex2 = 0;
int sortIndex = low;
while(temIndex2 <= temIndex-1)
{
sortArray[sortIndex] = tem[temIndex2];
temIndex2++;
sortIndex++;
}*/
System.out.println("重新移入");
outputArray(sortArray);
}
}
public static void main(String[] args)
{
// 测试数组 {9,8,7,5,1,2,3,12,3,5,88,99,78,90,78,66,543,77,54,9,3,21,125}
int[] sortArray = new int[] {9,8,7,5,1,2,3,12,3,5,88,99,78,90,78,66,543,77,54,9,3,21,125};
Sort test = new Sort();
// test.insertSort(sortArray);
// test.shellSort(sortArray);
// test.selectSort(sortArray);
// test.twoEleSelectSort(sortArray);
// test.heapSort(sortArray);
// test.bubbleSort(sortArray);
// test.bubbleSortAdvanced(sortArray);
// test.twoElementBubbleSort(sortArray);
// test.quickSort(sortArray, 0, sortArray.length-1);
test.mergeSort(sortArray, 0, sortArray.length-1);
test.outputArray(sortArray);
}
}