package paixu;
import java.util.Arrays;
//直接插入排序-从小到大排列
//直接插入排序是一种稳定的排序。
/*基本思想:对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余记录为无序序列,
接着从第二个记录开始,按照记录的大小依次将当前待处理的记录插入到其之前的有序序列中,
直到最后一个记录插入到有序序列为止。*/
/*时间复杂度,最差=平均=o(n^2),最好是o(n),空间复杂度是o(1)
当数据正序时,执行效率最好,每次插入都不用移动前面的元素,时间复杂度为o(n)
当数据反序时,执行效率最差,每次插入都要前面的元素后移,时间复杂度为o(n^2)
所以,数据越靠近正序,直接插入排序的算法性能越好*/
//由直接插入排序算法可知,我们在排序过程中,需要一个临时变量存储要插入的值,所以空间复杂度为 1
//直接插入排序的过程中,不需要改变相等元素的位置,所以是稳定的算法
public class InsertSort {
public static void InserSort(int[] array)
{
if(array.length<=0 || array==null)
return;
int i,j;
int key;//key为每次新的值
//第一个数肯定有序,从第2个数开始遍历,依次插入有序序列
for(i=1;i<array.length;i++)
{
//【0,i-1】有序,取出第i个数,与前面的i-1个数进行比较后,插入到合适的位置
key=array[i];
//因为前n-1个数是有序的,只要当前比较的数array[j]比key大,就把这个数后移
for(j=i-1;j>=0 && array[j]>key;j--)
{
array[j+1]=array[j];
}
array[j+1]=key;//空出位置后,将key插入到腾出的位置[i+1]
}
}
public static void main(String[] args) {
int a[] = { 38,65,97,76,13,27,49 };
InserSort(a);
System.out.println(Arrays.toString(a));
}
}
/*以数组{38,65,97,76,13,27,49}为例:
第一步插入38以后:[38] 65 97 76 13 27 49
第二步插入65以后:[38 65] 97 76 13 27 49
第三步插入97以后:[38 65 97] 76 13 27 49
第四步插入76以后:[38 65 76 97] 13 27 49
第五步插入13以后:[13 38 65 76 97] 27 49
第六步插入27以后:[13 27 38 65 76 97] 49
第七步插入49以后:[13 27 38 49 65 76 97]
*/
package paixu;
import java.util.Arrays;
//希尔排序-从小到大
//希尔排序称作是缩小增量排序,是一种插入排序,不稳定排序。
/*基本思想:先将待排序的数组分成多个子序列,使得每个子序列的元素个数相对较小,然后对各个子序列进行
直接插入排序,等整个待排序序列基本有序后,再对所有的元素进行依次插入排序。
即:把记录按步长gap分组,对每组记录采用直接插入排序方法进行排序。随着步长逐渐减小,所分成的组包含
的记录越来越多,当步长的值减小到1时,整个数据合成一组,构成一组有序记录,则完成排序。*/
/*具体步骤:
选择一个步长序列{t1,...,tk}
按照步长序列个数k,对待排序列进行k趟排序
每趟排序,根据对应的步长ti,将待排序列分割成ti个子序列,分别对各个子序列进行直接插入排序。*/
//时间复杂度,平均o(n^1.3),最好o(n),最差o(n^2),空间复杂度o(1)
public class ShellSort {
public static void ShellSort(int[] array) {
// TODO Auto-generated constructor stub
{
if(array.length<=0 || array==null)
return;
int len=array.length;
int i,j,key;
for(int gap=len/2;gap>0;gap/=2)//分组
{
//对每组进行插入排序
for(i=gap;i<len;i++)
{
key=array[i];
for(j=i-gap;j>=0 && array[j]>key;j=j-gap)
{
array[j+gap]=array[j];
}
array[j+gap]=key;//空出位置后,将key插入到腾出的位置[i+1]
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = {26,53,67,48,57,13,48,32,60,50};
ShellSort(a);
System.out.println(Arrays.toString(a));
}
}
/*以数组{26,53,67,48,57,13,48,32,60,50}为例,步长序列{5,3,1}为例:
初始关键字:26 53 67 48 57 13 48 32 60 50
第一趟:26->13
13 53 67 48 57 26 48 32 60 50
53->48
13 48 67 48 57 26 53 32 60 50
67->32
13 48 32 48 57 26 53 67 60 50
48->60
13 48 32 48 57 26 53 67 60 50
57->50
13 48 32 48 50 26 53 67 60 57
第二趟:13->48->53->57
13 48 32 48 50 26 53 67 60 57
48->50->67
13 48 32 48 50 26 53 67 60 57
32->26->60
13 48 26 48 50 32 53 67 60 57
48
13 48 26 48 50 32 53 67 60 57
第三趟:13 26 32 48 48 50 53 57 60 67
*/
package paixu;
import java.util.Arrays;
//简单选择排序是一种不稳定的排序
/*基本思想:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行
* 交换;接着对不包括第一个记录以外的其他记录进行第二轮比较,得到最小的记录与第二个记录进行位置交换
* 重复该过程,直到进行比较的记录只有一个数为止*/
/*基本过程:
(1)从待排序序列中,找到关键字最小的元素;
(2)如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
(3)从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
每趟排序中,将当前第i小的元素放在位置i上*/
//时间复杂度,平均=最好=最差=o(n^2),空间复杂度o(1)
/*简单选择排序的比较次数与序列的初始排序无关。 假设待排序的序列有 N 个元素,则比较次数总是N (N - 1) / 2。
而移动次数与序列的初始排序有关。当序列正序时,移动次数最少,为 0
当序列反序时,移动次数最多,为3N (N - 1) / 2
所以,综合以上,简单排序的时间复杂度为 O(N2)
简单选择排序需要占用 1 个临时空间,在交换数值时使用。*/
public class SelectSort {
public static void SelectSort(int[] array) {
// TODO Auto-generated constructor stub
{
if(array.length==0 || array==null)
return;
int i,j,lowindex;
int len=array.length;
//需要遍历获得最小值得次数
//要注意一点,当要排序N个数时,已经经过N-1次遍历后,已经是有序序列
for(i=0;i<len-1;i++)//从第一个数开始
{
lowindex=i;//用来保存最小值的索引
//寻找第i个小的数值
for(j=i+1;j<len;j++)//从第二个数开始寻找
{
if(array[j]<array[lowindex])//从小到大
lowindex=j;
}
//将当前的元素与它后面序列中的最小元素交换,也就是将最小的元素放在最前端
int temp = array[i];
array[i] = array[lowindex];
array[lowindex] = temp;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = {38,65,97,76,13,27,49 };
SelectSort(a);
System.out.println(Arrays.toString(a));
}
}
/*以数组{38,65,97,76,13,27,49}为例:
第一趟排序后:13 [65 97 76 38 27 49]
第二趟排序后:13 27 [65 97 76 38 49]
第三趟排序后:13 27 38 [65 97 76 49]
第四趟排序后:13 27 38 49 [65 97 76]
第五趟排序后:13 27 38 49 65 [97 76]
第六趟排序后:13 27 38 49 65 76 [97]
第七趟排序后:13 27 38 49 65 76 97
*/
package paixu;
import java.util.Arrays;
//堆排序-从小到大
/*堆是一棵顺序存储的完全二叉树,其中每个结点的关键字都不大于其孩子节点的关键字,这样的堆称为小根堆。
* 其中每个结点的关键字都不小于其孩子节点的关键字,这样的堆称为大根堆。*/
/*基本思想:
* 将无序列序列数组看作一棵顺序存储二叉树,然后建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
* 将堆顶元素与末尾元素交换,将最大(小)元素“沉”到数组末端;
* 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序*/
//主要过程:构建堆和交换堆顶元素与最后一个元素的位置(大根堆实现升序,小根堆实现降序)
//时间复杂度:平均=最差=最好=o(nlogn),空间复杂度o(1)
public class HeapSort {
public static void HeapSort(int[] array) {
// TODO Auto-generated constructor stub
{
if(array.length == 0 || array == null)
return;
int len = array.length;
//建立初始堆(大根堆)
//从第一个非叶子结点从下到上,从左到右调整结构
for(int i=len/2-1;i>=0;i--)//第一个非叶子结点的节点数是n/2-1
{
MaxHeadAdjust(array,i,len);
}
//调整堆结构+交换堆顶元素与末尾元素
for(int i=len-1;i>0;i--)
{
//最后一个元素与第一个元素进行交换
int temp=array[i];
array[i]=array[0];
array[0]=temp;
//重新调整堆(大根堆)
MaxHeadAdjust(array,0,i);
}
}
}
private static void MaxHeadAdjust(int[] array, int parent, int len) {
// TODO Auto-generated method stub
//保存当前父节点
int temp = array[parent];
//先获得左孩子
int child = 2 * parent + 1;
//从parent节点的左子节点开始,即2*parent+1处开始
for(child = 2 * parent + 1; child < len; child *= 2)
{
//如果左子节点小于右子节点,child指向右子节点
if(child + 1 < len && array[child] < array[child + 1])
{
child ++;
}
//如果子节点大于父节点,将子节点值赋给父节点
if(array[child] > temp)
{
array[parent] = array[child];
parent = child;
}
//如果父节点比孩子节点最大的那个还要大,就不需要调整
else
{
break;
}
}
array[parent] = temp;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 };
HeapSort(a);
System.out.println(Arrays.toString(a));
}
}
/*设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }
首先是构造初始堆:先根据无序序列建立完全二叉树——>发现5比8小,互换——>发现4比9小,互换——>发现3比8小,
互换。换位置后,和最大的孩子结点7相比还是小,再次互换,得到{1,8,9,7,2,6,4,3,5,0}——>
发现1比9小,互换,换到位置后,和最大的孩子结点6相比还是小,再次互换!——>此时,所有的父节点都比自己的子孙节点大
{9,8,6,7,2,1,4,3,5,0}
构造了初始堆,然后就进行完整的堆排序处理
交换9和0,输出9——>筛选调整——>交换8和0,输出8——>筛选调整——>交换7和0,输出7——>.....——>输出0,堆排序结束*/
package paixu;
import java.util.Arrays;
//冒泡排序--从小到大
//冒泡排序是一种交换排序,稳定排序
/*基本思想:假设要对一个大小为N的无序序列进行升序排序,对于给定的n个记录,从第一个记录开始依次对相邻的
* 两个记录进行比较,当前面的记录大于后面的记录时,交换位置,进行一轮比较和交换后,n个记录中最大记录
* 将位于第n位;然后对前n-1个记录进行第二轮比较;重复该过程直到进行比较的记录剩下一个为止。*/
/*(1) 每趟排序过程中需要通过比较找到第 i 个小的元素。所以,我们需要一个外部循环,从数组首端(下标 0) 开始,
一直扫描到倒数第二个元素(即下标 N - 2) ,剩下最后一个元素,必然为最大。
(2) 假设是第 i 趟排序,可知,前 i - 1 个元素已经有序。现在要找第 i 个元素,只需从数组末端开始,扫描到第 i 个
元素,将它们两两比较即可。所以,需要一个内部循环,从数组末端开始(下标 N - 1),扫描到 (下标 i + 1)。
* */
//时间复杂度,平均=最差=o(n^2),最好o(n),空间复杂度o(1)
/*冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之
间。所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。*/
public class BubbleSort {
public static void BubbleSort(int[] array) {
// TODO Auto-generated constructor stub
{
if(array.length == 0|| array == null)
return;
int i,j;
int len = array.length;
//用来交换的临时数
int temp;
//要遍历的次数
for(i = 0; i < len; i ++)
{
//从后往前依次比较相邻的两个数的大小,遍历一次后,将数组中第i小的数放在第i个位置上
//for(j = len - 1; j > i; j --)
//从前往后依次比较相邻的两个数的大小。
for(j = 1 ; j < len; j ++)
{
//比较相邻的元素,如果前面的数大于后面的数,则交换
if(array[j] < array[j - 1])
{
temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = {36,25,48,12,25,65,43,57};
BubbleSort(a);
System.out.println(Arrays.toString(a));
}
}
/*以数组{36,25,48,12,25,65,43,57}为例:
一趟排序的过程如下:
R[1]:25 36 48 12 25 65 43 57
R[2]:25 36 48 12 25 65 43 57
R[3]:25 36 12 48 25 65 43 57
R[4]:25 36 12 25 48 65 43 57
R[5]:25 36 12 25 48 65 43 57
R[6]:25 36 12 25 48 43 65 57
R[7]:25 36 12 25 48 43 57 65
初始状态:[36 25 48 12 25 65 43 57]
1趟排序:[25 36 12 25 48 43 57] 65
2趟排序:[25 12 25 36 43 48] 57 65
3趟排序:[12 25 25 36 43] 48 57 65
4趟排序:[12 25 25 36] 43 48 57 65
5趟排序:[12 25 25] 36 43 48 57 65
6趟排序:[12 25] 25 36 43 48 57 65
7趟排序:[12] 25 25 36 43 48 57 65
*/
package paixu;
import java.util.Arrays;
//快速排序-从小到大
/*基本思想:选取一个基准数,然后将大于和小于基准的元素分别置于基准数的两边;继续分别对按此方法分治基准数的两侧
* 直至整个序列有序。即:通过一趟排序将要排序的数据分割成独立的两部分:分割点左边都是比它小的数,右边都是比它大的数。然后
再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。*/
//时间复杂度,平均=最好=o(nlogn),最差o(n^2),空间复杂度o(nlogn)
public class QuickSort {
public static void QuickSort(int[] array, int left, int right) {
// TODO Auto-generated constructor stub
{
//该算法选取第一个数作为基准数,如果想要用中间数为基准数,那么将第一个数和中间数交换即可
if(left < right)
{
//将中间的这个数与第一个数交换swap(array[left],array[left+right]/2)
int i = left,j = right, key = array[left];//序列的下标
while(i < j)
{
//从右向左找第一个小于key的数
while(i < j && array[j] > key)
{
j--;
}
if(i < j)
{
array[i++] = array[j];//当从右向左找到后,i++
}
//从左往右找到第一个大于等于key的数
while(i < j && array[i] <= key)
{
i++;
}
if(i < j)
{
array[j--] = array[i];//当从左向右找到后,j--
}
}
array[i] = key;
QuickSort(array,left,i-1);
QuickSort(array,i+1,right);
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = {49,38,65,97,76,13,27,49 };
QuickSort(a,0,a.length-1);
System.out.println(Arrays.toString(a));
}
}
/*以数组{49,38,65,97,76,13,27,49}为例:
第1趟排序的过程如下:
初始化关键字:[49 38 65 97 76 13 27 49]
从后往前:
49 = 49
27 < 49进行交换[27 38 65 97 76 13 49 49]
从前往后:
38 < 49
65 > 49进行交换[27 38 49 97 76 13 65 49]
从后往前:
13 < 49进行交换[27 38 13 97 76 49 65 49]
从前往后:
97 > 49进行交换[27 38 13 49 76 97 65 49]
从后往前:
76 > 49
49 = 49-->left = right
[27 38 13] 49 [76 97 65 49]
初始化关键字:[49 38 65 97 76 13 27 49]
1趟排序后:[27 38 13] 49 [76 97 65 49]
2趟排序后:[13] 27 [38]] 49 [49 65] 76 [97]
3趟排序后:13 27 38 49 49 [65] 76 97
最后排序结果:13 27 38 49 49 65 76 97
*/
package paixu;
import java.util.Arrays;
//归并排序是一种稳定排序
/*归并排序是利用递归与分治技术将数据划分为越来越小的半子表,再对半子表排序,最后再利用递归方法将排序好的半子表合并成越来
* 越大的有序序列。
* 基本思想:对于给定的一组记录(假设n个记录),首先将每两个相邻长度为1 的子序列进行归并,得到n/2(向
上取整)个长度为2或1的有序子序列,再将其两两归并,反复执行此过程,直到得到一个有序序列。*/
//时间复杂度,平均=最差=最好=o(nlogn),空间复杂度o(n)
public class MergeSort {
public static void MergeSort(int[] array, int left, int right, int[] temp) {
// TODO Auto-generated constructor stub
{
if(left == right)
{
temp[left] = array[left];
}
else
{
int mid = (left + right)/2;
MergeSort(array, left, mid, temp);//左边有序
MergeSort(array, mid + 1, right, temp);//右边有序
Merge(array, left, right, mid,temp);//再将两个有序序列合并
}
}
}
private static void Merge(int[] array, int left, int right, int mid, int[] temp) {
// TODO Auto-generated method stub
int i = left;//第一段序列的下标
int j = mid + 1;//第二段序列的下标
int k = 0;//临时存放合并序列的下标
while(i <= mid && j<= right)//扫描第一段和第二段序列,直到有一个扫描结束
{
//判断第一段和第二段取出的数哪个更小,将其存入合并序列,并继续向下扫描
if(array[i] <= array[j])
{
temp[k] = array[i];
i++;
k++;
}
else
{
temp[k] = array[j];
j++;
k++;
}
}
//若第一段序列还没扫描完,将其全部复制到合并序列
while(i <= mid)
{
temp[k ++] = array[i ++];
}
//若第二段序列还没扫描完,将其全部复制到合并序列
while(j <= right)
{
temp[k ++] = array[j ++];
}
// 将合并序列复制到原始序列中
for (k = 0, i = left; i <= right; i++, k++)
{
array[i] = temp[k];
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[] = {49,38,65,97,76,13,27,49 };
int[] tmp=new int[a.length];
MergeSort(a,0,a.length-1,tmp);
System.out.println(Arrays.toString(a));
}
}
/*以数组{49,38,65,97,76,13,27}为例:
初始关键字:[49][38][65][97][76][13][27]
第一趟归并后:[38 49] [65 97] [13 76] [27]
第二趟归并后:[38 49 65 97] [13 27 76]
第三趟归并后:[13 27 38 49 65 76 97]
*/
//若从空间复杂度来考虑:首选堆排序,其次是快速排序,最后是归并排序。
//若从稳定性来考虑,应选取归并排序,因为堆排序和快速排序都是不稳定的。
//若从平均情况下的排序速度考虑,应该选择快速排序。