快速排序
算法思想
快速排序是对冒泡排序的一种改进,是基于分治法的思想,通常选排序列表第一个元素为中轴元素,将列表元素先从后向前扫描指针high,找到小于中轴元素的交换到前面,再将列表元素先从前向后扫描指针low,找到大于中轴元素的交换到后面,直到 low=high,把中轴元素定位到最终位置low处,此过程称为一趟快排,而后分别递归的对两个子表重复上述过程,直到每部分元素只有一个或为空。
快速排序之所比较快,因为相比冒泡排序,每次交换是跳跃式的。每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边。这样在每次交换的时候就不会像冒泡排序一样每次只能在相邻的数之间进行交换,交换的距离就大的多了。因此总的比较和交换次数就少了,速度自然就提高了。当然在最坏的情况下,仍可能是相邻的两个数进行了交换。因此快速排序的最差时间复杂度和冒泡排序是一样的都是O(N2),它的平均时间复杂度为O(NlogN)。其实快速排序是基于一种叫做“二分”的思想
复杂度与稳定性分析
空间效率:由于快排基于递归,所以借用递归工作栈
- 最坏情况:O(n) 要进行n-1次递归调用
- 平均情况:O(logn)
时间效率:快排运行时间与划分是否对称有关
- 最坏情况:O(n²) 当排序表基本有序或逆序时,导致极端划分,n-1 pivot 0 || 0 pivot n-1
- 最好情况:O(nlogn) 平均划分的情况,一半一半
稳定性
- 不稳定 交换前后相同的关键字相对位置会发生改变
代码实现
public static void QuickSort(int[] arr,int low,int high){
if(low<high){
int pivotpos=Partition(arr,low,high);
QuickSort(arr,low,pivotpos-1);
QuickSort(arr,pivotpos+1,high);
}
}
private static int Partition(int[] arr, int low, int high) {
int pivot =arr[low];
while(low<high){
while(low<high&&arr[high]>=pivot){
high--;
}
arr[low]=arr[high];
while (low<high&&arr[low]<=pivot){
low++;
}
arr[high]=arr[low];
}
arr[low]=pivot;
return low;
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("快速排序之前:"+ Arrays.toString(arr));
QuickSort(arr,0,arr.length-1);
System.out.println("快速排序之后:"+ Arrays.toString(arr));
}
冒泡排序
算法思想
- 比较前后相邻的两个数,如果前面的数大于后面的数,就将这两个数交换
- 对数组的第0个到n-1个数进行一次遍历后,最大的一个数就沉到饿了数组第n-1个位置
- 沉到后边的数即为排好的数,不予理会,继续排前n=n-1个数,重复上述过程,直到所有数排完
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 最坏情况:O(n²)
- 平均情况:O(n²)
稳定性
- 稳定
代码实现
public class bubblesort {
public static void BubbleSort(int[] arr){
for(int i=0;i<arr.length;i++){
for(int j=1;j<arr.length-i;j++){
if(arr[j-1]>arr[j]){
int temp;
temp=arr[j-1];
arr[j-1]=arr[j];
arr[j]=temp;
}
}
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("冒泡排序之前:"+ Arrays.toString(arr));
BubbleSort(arr);
System.out.println("冒泡排序之后:"+ Arrays.toString(arr));
}
归并排序
算法思想
将两个或两个以上的有序子表合并成一个新的有序表,若原待排表有n个记录,则看成n个有序的子表,每个子表长度为1,相邻子表两两归并,得到n/2个长度为2或1的子表,再继续两两归并…直到合并成一个长度为n的有序表为止,这是二路归并。
复杂度与稳定性分析
空间效率:
- O(n) : 辅助数组占用n个单元
时间效率:
- 平均情况:O(nlogn) 归并一趟为O(n),共需要logn趟归并
稳定性
- 稳定 Merge()操作不会改变相同关键字的相对顺序
代码实现
public static void Merge(int [] arr,int low,int center,int high){
int [] tmpArr=new int[arr.length]; //创建临时数组
int k =low; //记录临时数组的索引
int tmp=low; //缓存左数组第一个元素的索引
int mid=center+1; //右数组第一个元素索引
while(low<= center&& mid<=high){
//从两个数组中取出最小的放入临时数组
if(arr[low]<=arr[mid]){
tmpArr[k++]=arr[low++];
}else{
tmpArr[k++]=arr[mid++];
}
}
//剩下的部分依次放入临时数组,只会执行一个while
while (low<=center){
tmpArr[k++]=arr[low++];
}
while (mid<=high){
tmpArr[k++]=arr[mid++];
}
//将临时数组的内容拷贝回原数组(原low,high范围的内容排序完被复制回原数组)
while(tmp<=high){
arr[tmp]=tmpArr[tmp++];
}
}
public static void MergeSort(int[] arr,int low,int high){
if(low<high){
int center =(low+high)/2;
MergeSort(arr,low,center);
MergeSort(arr,center+1,high);
Merge(arr,low,center,high);
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("归并排序之前:"+ Arrays.toString(arr));
MergeSort(arr,0,arr.length-1);
System.out.println("归并排序之后:"+ Arrays.toString(arr));
}
直接插入排序
算法思想
从待排序列的第二个元素(默认第一个元素已经有序)开始,往前面已经有序的序列里插,先保存当前待插入元素,再与它前面所有元素逐个比较,比它大的往后面排,直到找到小于等于它的第一个元素,插入到后面即可。
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 最坏情况:O(n²)
- 最好情况:O(n) 此时序列已经有序,每插入一个元素只需比较一次,而不需移动元素
稳定性
- 稳定
代码实现
public static void InsertSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
if(arr[i]<arr[i-1]){
int insertVal=arr[i];
int index=i-1;
while(index>=0&&insertVal<arr[index]){
arr[index+1]=arr[index];
index--;
}
arr[index+1]=insertVal;
}
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("直接插入排序之前:"+ Arrays.toString(arr));
InsertSort(arr);
System.out.println("直接插入排序之后:"+ Arrays.toString(arr));
}
折半插入排序
算法思想
折半插入指的是定位待插入位置的时候用折半算法,减少了比较次数,找到待插入位置之后,把比待插入元素大的都依次挪到它后边
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 平均情况:O(n²) 仅仅减少了比较次数,而元素的移动次数没有改变
稳定性
- 稳定
代码实现
public static void BinaryInsert(int arr[]){
for (int i = 1; i <arr.length ; i++) {
int insertVal = arr[i];
int low=0,high=i-1;
while(low<=high){
int mid=(low+high)/2;
if(insertVal<arr[mid]){
high=mid-1;
}else{
low=mid+1;
}
}
int index=i-1;
while(index>=low){
arr[index+1]=arr[index];
index--;
}
arr[low]=insertVal;
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("折半插入排序之前:"+ Arrays.toString(arr));
BinaryInsert(arr);
System.out.println("折半插入排序之后:"+ Arrays.toString(arr));
}
折半插入排序
算法思想
希尔排序又称为缩小增量排序,先将待排序列分割成若干个形如L [i,i+d,i+2d…,i+kd]的子表,分别进行直接插入排序,当整个表元素基本有序时,再对全体元素进行依次直接插入排序。
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 最坏情况:O(n²)
稳定性
- 不稳定
代码实现
public static void ShellSort(int[] arr){
for(int dk=arr.length/2;dk>=1;dk--){ //只比直接插入排序多这一行,直接插入排序默认dk=1
for (int i = dk; i < arr.length; i++) {
if(arr[i]<arr[i-dk]){
int insertVal=arr[i];
int index=i-dk;
while(index>=0&&insertVal<arr[index]){
arr[index+dk]=arr[index];
index-=dk;
}
arr[index+dk]=insertVal;
}
}
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("希尔排序之前:"+ Arrays.toString(arr));
ShellSort(arr);
System.out.println("希尔排序之后:"+ Arrays.toString(arr));
}
选择排序
算法思想
从待排数组中选择最小元素,将它与数组的第一个元素交换位置。再从待排数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 平均情况:O(n²)
稳定性
- 不稳定
代码实现
public static void SelectSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
int min=i;
for (int j = i+1; j <arr.length; j++) {
if(arr[j]<arr[min]){
min=j;
}
}
if(min!=i){
int temp=arr[i];
arr[i]=arr[min];
arr[min]=temp;
}
}
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("选择排序之前:"+ Arrays.toString(arr));
SelectSort(arr);
System.out.println("选择排序之后:"+ Arrays.toString(arr));
}
堆排序
算法思想
首先构造全部元素的大顶堆,把堆顶元素与待排数组最后一个元素交换,再把剩余的待排数组重新构造大顶堆,交换,重复此过程,直到数组全部有序。
复杂度与稳定性分析
空间效率:
- O(1)
时间效率:
- 平均情况:O(nlogn)
稳定性
- 不稳定
代码实现
public static void HeapSort(int[]arr){
BuildMaxHeap(arr); //初始建堆
for (int i = arr.length-1; i >0 ; i--) { //n-1趟交换和建堆的过程
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
AdjustDown(arr,0,i); //调整,把剩下的i个元素重新整理成堆
}
}
public static void BuildMaxHeap(int []arr){
for (int i = (arr.length-1)/2; i >=0 ; i--) {
AdjustDown(arr,i,arr.length);
}
}
public static void AdjustDown(int[]arr,int parent,int len){
int tp=arr[parent];
int lchild=2*parent+1;
while(lchild<len){
int rchild=lchild+1;
if(rchild<len&&arr[lchild]<arr[rchild]){
lchild++;
}
if(tp>arr[lchild]){
break;
}
arr[parent]=arr[lchild];
parent=lchild;
lchild=lchild*2+1;
}
arr[parent]=tp;
}
public static void main(String[] args) {
int[] arr=new int[10];
for (int i = 0; i <arr.length; i++) {
arr[i]=new Random().nextInt(20);
}
System.out.println("堆排序之前:"+ Arrays.toString(arr));
HeapSort(arr);
System.out.println("堆排序之后:"+ Arrays.toString(arr));
}