数据结构与排序算法

基本概念

数据结构

数据结构:存储数据的不同方式或组织存储数据的集合,分为逻辑结构和物理结构
逻辑结构:集合结构、线性结构、树形结构、图形结构
物理结构:顺式结构、链式结构

算法

算法:同一问题的不同解决方法,算法往往是解决特定的数据结构
测算算法优劣:时间、 空间

如何写算法程序

由简单到复杂:验证一步走一步、多打印中间结果
先局部后整体:没思路时先细分
先粗糙后精细:变量更名、语句合并、边界处理

时间复杂度分析

算法随着输入规模增长量时(算法函数中规律):
常数可以忽略
最高次幂的常数因子可以忽略
最高次幂越小,算法效率越高
大O记法
用常数1取代所有运行时间中的所有加法常数
只保留最高阶项
去除最高阶项系数

空间复杂度分析

基本数据类型内存占用字节数
byte1
boolean1
char2
short2
int4
float4
long8
double8

1、计算机访问内存一次1字节
2、一次引用8个字节
3、创建对象16个字节
4、不足8个字节时,自动填充8个字节(20个字节填充为24个字节)

排序算法宋词记忆法

选泡插
快归堆希统计基
恩方恩老恩一三
对恩加 k 恩 乘 k
不稳稳稳不稳稳
不稳不稳稳稳稳

Comparable接口:Java提供的用来定义排序规则

算法

选择排序

思想:每遍历一次列表找到最小的数字,将最小数字和最左边交换,依次从左到右进行
动画展示:
在这里插入图片描述

最初版:

public class selectionsort {
	public static void main(String[] args) {
		int [] arr={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
		for(int i=0;i<arr.length;i++){
			int minpos=i;
			for (int j = i+1; j < arr.length; j++) {
				if (arr[minpos]>arr[j]){
					minpos=j;
				}
			}
			int temp=arr[i];
			arr[i]=arr[minpos];
			arr[minpos]=temp;	
		}
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

修改版:

public class selectionsort2 {
	public static void main(String[] args) {
		int[] arr={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
		for (int i = 0; i < arr.length-1; i++) {
			int minpos=i;
			for (int j = i+1; j < arr.length; j++) {
				minpos=arr[j]<arr[minpos]?j:minpos;	
			}
			System.out.print("minpos:"+minpos);
			swap(arr, i, minpos);
			System.out.println("经过第"+i+"次变换后,所得数组为:");
			show(arr);
		}	
	}
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

冒泡排序(常考)

思想:不断交换使大的数据后移,使最大数据归位
图形展示:
在这里插入图片描述

代码展示:

public class BubbleSort {
	public static void main(String[] args) {
		int[] arr={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
		sort(arr);
		show(arr);
	}
	public static void sort(int[] arr){
		for (int i = arr.length-1; i >0; i--) {
			findMax(arr, i);
		}
	}
	public static void findMax (int[] arr,int n) {
		for (int j = 0; j < n; j++) {
			if(arr[j]>arr[j+1]){
				swap(arr, j, j+1);
			}
		}
	}
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

插入排序(常考)

思想:从左往右依次把最小的数移到最前面。
图形展示:

在这里插入图片描述

代码展示:

public class InsertionSort {
	public static void main(String[] args) {
		int[] arr={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
		sort(arr);
		show(arr);
	}
	public static void sort(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			for (int j = i; j >0 && arr[j]<arr[j-1]; j--) {
				swap(arr, j, j-1);
			}
		}
	}
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

简单排序算法总结

冒泡:基本不用,太慢
选择:基本不用,不稳
插入:样本小且基本有序的时候效率高

希尔排序

思想:先给一个间隔,排序,间隔不断缩小排序,最后间隔为1
图形展示:
间隔为4
在这里插入图片描述
间隔为2,为1,最后间隔为1是用了插入排序:
在这里插入图片描述knuth序列:

h=1
h=3*h+1

代码展示:

public class ShellSort {
	public static void main(String[] args) {
		int[] arr={9,6,11,3,5,12,8,7,10,15,14,4,1,13,2};
		sort(arr);
		show(arr);
	}
	public static void sort(int[] arr){
		int h=1;
		while(h<arr.length/3){
			h=h*3+1;
		}
		for (int gap = h; gap >0; gap=(gap-1)/3) {
			for (int i = gap; i < arr.length; i++) {
				for (int j = i; j > gap-1 && arr[j]<arr[j-gap]; j-=gap) {
					swap(arr, j, j-gap);
				}
			}
		}	
	}
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

归并排序(常考)

思想:将已有序的子序列合并,得到完全有序的序列
图形展示:
在这里插入图片描述

代码展示:

public class Mergesort1 {
	public static void main(String[] args) {
		int[] arr={1,4,7,8,3,6,9};
		sort(arr);	
	}
	public static void sort(int[] arr){
			Merge(arr);
	}
	public static void Merge (int[] arr) {
		int mid=arr.length/2;
		int[] temp=new int[arr.length];
		
		int i=0;
		int j=mid+1;
		int k=0;
		
		while(i<=mid &&j<arr.length){
			temp[k++]=arr[i]<=arr[j]?arr[i++]:arr[j++];
		}
		while(i<=mid)temp[k++]=arr[i++];
		while(j<arr.length)temp[k++]=arr[j++];
		show(temp);
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

进阶版(递归思想):

public class Mergesort2 {
	public static void main(String[] args) {
		int[] arr={1,4,7,8,3,6,9};
		sort(arr,0,arr.length-1);
		show(arr);
	}
	public static void sort(int[] arr,int left,int right){
			if(left==right) return;
			//分成两半
			int mid=left+(right-left)/2;
			//左边排序
			sort(arr, left, mid);
			//右边排序
			sort(arr, mid+1, right);
			Merge(arr,left,mid+1,right);
	}
	public static void Merge (int[] arr,int leftPtr,int rightPtr,int rightBound) {
		int mid=rightPtr-1;
		int[] temp=new int[rightBound-leftPtr+1];
		
		int i=leftPtr;
		int j=rightPtr;
		int k=0;
		
		while(i<=mid &&j<=rightBound){
			temp[k++]=arr[i]<=arr[j]?arr[i++]:arr[j++];
		}
		while(i<=mid)temp[k++]=arr[i++];
		while(j<=rightBound)temp[k++]=arr[j++];
		for (int m = 0; m < temp.length; m++) {
			arr[leftPtr+m]=temp[m];
		}
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

快速排序(常考)

思想:从数列中挑出一个元素,比它小的排在前面,比它大的排在后面
图形展示:
在这里插入图片描述

代码展示:

public class QuickSort {
	public static void main(String[] args) {
		int[] arr={7,3,2,8,1,9,5,4,6,10,6};
		sort(arr,0,arr.length-1);
		show(arr);
	}
	public static void sort(int[] arr,int leftbound,int rightbound){
		if(leftbound>rightbound) return;
		int mid=Partition(arr,leftbound,rightbound);
		sort(arr, leftbound, mid-1);
		sort(arr, mid+1, rightbound);
	}
	public static int  Partition (int[] arr,int leftbound,int rightbound) {
		int pivot=arr[rightbound];
		int left=leftbound;
		int right=rightbound-1;
		while(left<=right){
			while( left<=right && arr[left]<=pivot)left++;
			while(left<=right && arr[right]>pivot)right--;
			if(left<right) swap(arr, left, right);
		}
		swap(arr, left, rightbound);
		return left;
	}
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
	public static void show(int[] arr){
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
	}
}

计数排序

思想:统计各个数字出现的次数,如高考成绩排名
代码展示:

package sort;
import java.util.Arrays;
public class CountSort1 {
	public static void main(String[] args) {
		int[] arr={2,4,2,3,7,1,1,0,0,5,6,9,5,7,4,0,8,9};
		int[] result=sort(arr);
		System.out.println(Arrays.toString(result));
	}
	public static int[] sort(int[] arr){
		int[] result=new int [arr.length];
		int [] count=new int [10];
		for (int i = 0; i < arr.length; i++) {
			count[arr[i]]++;
		}
		System.out.println(Arrays.toString(count));
		for (int i = 0, j=0; i < count.length; i++) {
			while(count[i]-->0){
				result[j++]=i;
			}
		}
		System.out.println(Arrays.toString(result));
		return result;
	}
}

优化性能:
增加了所处位置的排名
在这里插入图片描述

import java.util.Arrays;
public class CountSort {
	public static void main(String[] args) {
		int[] arr={2,4,2,3,7,1,1,0,0,5,6,9,5,7,4,0,8,9};
		int[] result=sort(arr);
		System.out.println(Arrays.toString(result));
	}
	public static int[]  sort(int[] arr){
		int[] result=new int[arr.length];
		int[] count=new int[10];
		for(int i=0;i<arr.length;i++){
			count[arr[i]]++;	
		}
		System.out.println(Arrays.toString(count));
		//增加了此处的排名
		for (int i = 1; i < count.length; i++) {
			count[i]=count[i]+count[i-1];
		}
		System.out.println(Arrays.toString(count));
		for (int i = arr.length-1; i>0; i--) {
			result[--count[arr[i]]]=arr[i];
		}
		 return result;
	}	
}

基数排序

思想:数字从低位到高位依次排序
图形展示:
在这里插入图片描述

代码展示:

package sort;
import java.util.Arrays;
public class RadixSort1 {
	public static void main(String[] args) {
		int[] arr={421,240,115,532,305,430,1141};
		int[] result=sort(arr);
		System.out.println(Arrays.toString(result));
	}
	public static  int [] sort(int[] arr){
		int [] result=new int[arr.length];
		int [] count=new int[10];
		int h=getnum(arr);
		for (int i = 0; i < h; i++) {
			int division=(int)Math.pow(10, i);
			System.out.println("按照的位数排"+division);
			for (int j = 0; j < arr.length; j++) {
				int num=arr[j]/division%10;
				System.out.print(num+" ");
				count[num]++;
			}
			System.out.println(Arrays.toString(count));
			//计数排序
			for (int m = 1; m < count.length; m++) {
				count[m]=count[m]+count[m-1];
			}
			System.out.println(Arrays.toString(count));
			for (int n = arr.length-1; n >=0; n--) {
				int num=arr[n]/division%10;
				result[--count[num]]=arr[n];
			}
			System.arraycopy(result, 0, arr, 0, arr.length);
			Arrays.fill(count, 0);
		}
		return result;
	}
	//利用冒泡排序的方法找到最大值,并计算其位数。
	public static int getnum(int[] arr){
		findmax(arr, arr.length-1);
		int h=arr[arr.length-1];
		int k=0;
		while(h>0){
			h=h/10;
			k++;
		}
		return k;
	}
	public static void findmax(int[] arr,int n)	{
		for (int i = 0; i < n; i++) {
			if (arr[i]>arr[i+1]) {
				swap(arr, i, i+1);	
			}
		}
	}	
	public static void swap(int[] arr,int i,int j){
		int temp=arr[i];
		arr[i]=arr[j];
		arr[j]=temp;
	}
}

总结排序算法(面试)

在这里插入图片描述
外部排序和内部排序有哪些?(面试)

内部排序:待排序记录存放在计算机随机存储器中(说简单点,就是内存)进行的排序过程。
外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需要对外存进行访问的排序过程
内部排序:插入排序、快速排序、选择排序、归并排序、基数排序等

外部排序基本上由两个相对独立的阶段组成。首先,按可用内存大小,将外存上含n个记录的文件分成若干长度为l的子文件或段,依次读入内存并利用有效的内部排序方法对他们进行排序,并将排序后得到的有序子文件重新写入外存,通常称这些有序子文件为归并段;然后,对这些归并段进行逐趟归并,使归并段逐渐由小至大,直至得到整个有序文件为止。

排序算法的稳定性及其汇总:
同样值的个体之间,如果不因排序而改变相对次序,就是这个排序具有稳定性,否则没有。
不具有稳定的排序:选择,快速,堆,希尔
具有稳定性的排序:冒泡,插入,归并,一切桶排序思想;

选择排序:数组中找到最小的,移到最前面去,不稳定;
冒泡排序:从左到右,一直两两比较,小的在前面,大的在后面,相等时,不用换,稳定;
插入排序:从左往右依次把最小的数移到最前面,如果右边的数不比左边的小,就停止,稳定;
归并排序:将已有序的子序列合并,保证相同的时候,都是先放第一个的,再放第二个,稳定;
快速排序:小于等于放左边,大于放右边:比如67663,5,然后3就会和第一个6交换,不稳定;
小于在左边,大于在右边,等于在中间;比如55553,5,就会把第一个5和3交换,不稳定;
堆排序:不稳定,5446,第一个4就要和6交换;

排序算法时间空间稳定
选择 O ( N 2 ) O(N^{2}) O(N2) O ( 1 ) O(1) O(1)不稳定
冒泡 O ( N 2 ) O(N^{2}) O(N2) O ( 1 ) O(1) O(1)稳定
插入 O ( N 2 ) O(N^{2}) O(N2) O ( 1 ) O(1) O(1)稳定
归并 O ( N ∗ L o g N ) O(N*{LogN}) O(NLogN) O ( N ) O(N) O(N)稳定
快速排序(随机) O ( N ∗ L o g N ) O(N*{LogN}) O(NLogN) O ( L o g N ) O(LogN) O(LogN)不稳定
O ( N ∗ L o g N ) O(N*{LogN}) O(NLogN) O ( 1 ) O(1) O(1)不稳定

常见的坑
1,归并排序的额外空间复杂度可以变成O(1) ,但是非常难,不需要掌握,有兴趣可以搜“归并排序内部缓存法”&on·。
2,“原地归并排序”的帖子都是垃圾,会让归并排序的时间复杂度变成O(N^{2})
3,快速排序可以做到稳定性问题,但是非常难,不需要掌握,可以搜“01stable sort”
4,所有的改进都不重要,因为目前没有找到时间复杂度O(N*logN),额外空间复杂度O(1),又稳定的排序。
5,有一道题目,是奇数放在数组左边,偶数放在数组右边,还要求原始的相对次序不变,碰到这个问题,可以怼面试官。

总结
最坏情况、最好情况、排序方式不用记
插入排序、归并排序、堆排序、快速排序需要重点掌握
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值