public class Sort {
/**
* 名称:冒泡排序
* 顺序:从小到大(非必须)
* 逻辑:循环比较数组中相邻的数,较大的数放在下标较大的位置。比较的时候就会发生移动
* 原则:找到最大的数,放到最右边
* 例子:假如数组为{3,2,1}
* 先把3和2比较,换位置{2,3,1},再3和1比较,换位置{2,1,3}
* 再2和1比较,换位置{1,2,3}
*/
public static void bubbleSort(int[] a) {
boolean isContinue;
int num = 0;
for (int i = 0; i < a.length; i++) {
isContinue = false;
for (int j = 0; j < a.length - 1 - i; j++) {
if (a[j] > a[j + 1]) {
num = a[j];
a[j] = a[j + 1];
a[j + 1] = num;
isContinue = true;
}
}
// 如果没有发生位置变化,不需要再继续比了。
if (!isContinue)
break;
}
}
/**
* 名称:选择排序
* 顺序:从左到右,小到大(非必须)
* 逻辑:循环从左到右依次比较找到最小的数,跟循环次数N-1的下标位置的数互换。一次完整的比较移动一次
* 原则:找到最小的数,放到最左边
* 例子:假如数组为{3,2,1}
* 先把3和2比较,找到2,再2和1比较,找到1,然后1,3换位置{1,2,3}
* 再2和3比较,结束
*/
public static void selectSort(int[] a) {
int min;
for (int i = 0; i < a.length; i++) {
min = i;
for (int j = i + 1; j < a.length; j++) {
if (a[j] < a[min])
min = j;
}
int num = a[i];
a[i] = a[min] ;
a[min] = num ;
}
}
/**
* 名称:插入排序
* 顺序:从左到右,小到大
* 逻辑:从第二个开始,依次与左边的比较,遇到比自己大的时位置互换。
* 原则:在这个数的左边找到自己的位置
* 例子:假如数组为{3,2,1}
* 基数2,先把2和3比较,换位置{2,3,1}
* 再基数1,先1和3比较,换位置{2,1,3},再1和2比较,换位置{1,2,3}
*/
public static void insertSort(int[] a) {
for(int i = 1; i < a.length; i++) {
for(int j = i; j > 0; j--) {
if(a[j] < a[j-1]){
int num = a[j];
a[j] = a[j-1] ;
a[j-1] = num ;
}
}
}
}
/**
* 名称:归并排序
* 顺序:从左到右,小到大
* 逻辑:把一个数组分成两半,再分成两半,一直分,直到每一半只有1个元素,
* 然后把分出来的两个只有1个元素的数组排序合并成一个数组,
* 然后继续排序合并直到完成整个排序。
* 注意这里是把下标分段。
* 原则:分段比较
* 例子:假如数组为{8,4,2,7,9,10}
* 先分成{8,4,2}{7,9,10}
* 在分成{8,4}{2}{7,9}{10}
* 在分成{8}{4}{2}{7}{9}{10}
* {8}{4}是一个分出来的,先比较{8}{4},成为{4,8}
* 在与{2}比较,成为{2,4,8}。右边成为{7,9,10},
* 在比较{2,4,8}{7,9,10},
* 拿这个当例子说一下比较过程,上面也是一样的
* 2跟7比,把小2复制到临时数组backup{2}
* 然后4跟7比,把小4复制到backup{2,4}
* 然后8跟7比,把小7复制到backup{2,4,7}
* 然后8跟9比,把小8复制到backup{2,4,7,8}
* 然后发现左边的比完了,没数据了,那就把右边的全都直接复制到backup
* 成为{2,4,7,8,9,10}
*/
public static void mergeSort(int[] a){
int[] backup = new int[a.length];
recMergeSort(backup, 0, a.length-1, a);
}
private static void recMergeSort(int[] backup ,int startIndex,int endIndex,int[] a){
if(startIndex == endIndex){
return ;
}else{
int mid = (startIndex + endIndex)/2 ;
recMergeSort(backup, startIndex, mid, a);
recMergeSort(backup, mid+1, endIndex, a);
merge(backup,startIndex,mid+1,endIndex,a);
}
}
private static void merge(int[] backup ,int startIndex,int midIndex,int endIndex,int[] a){
int j = 0;
int start = startIndex;
int mid = midIndex-1;
int n = endIndex-startIndex+1 ;//当前数组的长度
//下面是排序合并
//当比较的两半都有数据,即下标小于等于这一半的最大下标
while (startIndex<=mid && midIndex<=endIndex) {
if(a[startIndex]<a[midIndex]){
backup[j++] = a[startIndex++];
}else{
backup[j++] = a[midIndex++];
}
}
//当有一半比完了,把剩下没比的放到最后
while (startIndex<=mid) {
backup[j++] = a[startIndex++];
}
//当有一半比完了,把剩下没比的放到最后
while (midIndex<=endIndex) {
backup[j++] = a[midIndex++];
}
//排序合并完复制到原数组的相应段落位置上
for(j=0;j<n;j++){
a[start+j] = backup[j] ;
}
}
/**
* 名称:希尔排序
* 顺序:从小到大
* 逻辑:就是插入排序,只不过把比较间隔由1变成了更大的数,把间隔相同的数排序,然后缩小间隔直到比较间隔为1
* 取间隔的公式又叫间隔序列公式(h = h * 3 + 1),用这个公式递归,知直到h为小于数组长度的最大数
* 原则:插入排序,当间隔大时,比较操作少,移位操作远。已经基本有序。当间隔小时,比较操作少,移位操作少
* 例子:假如数组为{8,4,2,7,9,10,30,32,5,11},长度10
* 那么间隔序列为4,1(用4计算可知下一个数为13,间隔比数组长度还大,不用比了。所以是4,1)
* 先4增量排序
* 9.8不动(8<9),10.4不动,30.2不动,32.7不动,5.9换位置{8,4,2,7,5,10,30,32,9,11}
* 既然9位置的数变小了,那要再比较一次9.8,现在变成了5.8比较,换位置{5,4,2,7,8,10,30,32,9,11}
* 接着刚才的5.9,11.10不动。4增量排序结束。整个过程换了895的位置,减少了插入排序位置移动的次数
* 再1增量排序,也就是插入排序
*/
public static void shellSort(int[] a){
int inner ,outer,temp;
int h = 1 ;
int arrLengthDecideThree = (a.length) / 3;
//用间隔序列公式(h = h * 3 + 1)取小于数组长度的最大序列数
//1,4,13,40,121……
while(h<=arrLengthDecideThree){
h = h * 3 + 1;
}
//用最大h增量排序,完成一次,用公式h = (h-1)/3取第二大序列数,再进行增量排序
//递减h,直到h等于1。……13,4,1
//一次循环是同一类增量排序,最后一次为1增量,也就是插入排序
while(h>0){
//跟插入排序一样,从第二个数开始,依次往左比较,
//这里的第二个数下标为,第一个数下标0+增量h,往左比较完,就取第三个数,也就是h+1……
for(outer=h;outer<a.length;outer++){
temp = a[outer] ;
inner = outer ;
//假如有10个数,那么就先用4增量,分别比较a[4]a[0],a[5]a[1],a[6]a[2],a[7]a[3]
//当inner++到8的时候,比较a[8]a[4],如果a[4]小于a[8],那就比较[9][5]
//如果a[4]大于a[8],那就把两个互换,得到较小的a[4],再a[4]a[0]比较
while (inner>h-1 && a[inner-h]>=temp) {
a[inner] = a[inner-h] ;
inner = inner - h ;
}
a[inner] = temp ;
}
h = (h-1) / 3 ;
}
}
/**
* 名称:快速排序
* 顺序:从小到大
* 逻辑:把数组分成小于最后一个数的一边和大于最后一个数的一边,找到最后一个数的位置。然后被分出来的两边继续这样操作。
* 原则:递归分成大小两边,当两边同时阻塞,互换元素位置。
* 例子:假如数组为{8,4,2,7,9,10,30,32,5,11},长度10
* 分成大于11和小于11的两块
* 左边{8,4,2,7,9,10}遇到30阻塞,右边直接就遇到5阻塞了,把30跟5换位,{8,4,2,7,9,10,5,32,30,11}
* 左边{8,4,2,7,9,10,5}遇到32阻塞,右边{32,30}遇到5阻塞,这时候整个数组都跟11比过一次了
* 把11跟32的位置互换{8,4,2,7,9,10,5,11,30,32},找到了11的位置,不会再改变。
* 下面继续这样处理11左边,和11右边的。
*/
public static void quickSort(int[] a){
_quickSort(0,a.length-1, a);
}
private static void _quickSort(int left,int right,int[] a){
if(right-left<=0){
return ;
}else{
int lastElement = a[right];
int lastElementFixIndex = parition(left,right,lastElement,a);
//从上面得到的数位置把数组分成两段,递归上面的操作
_quickSort(left, lastElementFixIndex-1, a);
_quickSort(lastElementFixIndex+1, right, a);
}
}
private static int parition(int left,int right,int lastElement,int[] a){
int _left = left -1 ;
int _right = right ;
while(true){
//直到遇到大的数停止
while(a[++_left]<lastElement){}
//直到遇到小的数停止
while(_right>0 && a[--_right]>lastElement){}
//整个数组都处理完了,跳出循环
if(_left>=_right){
break;
}else{
//大的数和小的数位置互换,小的放左边。继续进入while(true),加_left,减_right,直到break
swap(_left,_right,a);
}
}
//_left>=_right,break时,_left位置的数其实是大于right位置的数,因为他是遇到大的才停止。
//换位置,这个基数找到了自己的位置,且之后不再变化,返回下标
swap(_left,right,a);
return _left ;
}
private static void swap(int index1,int index2,int[] a){
int temp = a[index1] ;
a[index1] = a[index2] ;
a[index2] = temp ;
}
public static void main(String[] args) {
int[] a = { 4, 5, 6, 3, 8, 1, 9 };
shellSort(a);
for (int b : a) {
System.out.print(b);
}
int[] c = { 4, 5, 6, 3, 8, 1, 9 };
quickSort(c);
for (int b : c) {
System.out.print(b);
}
}
}