数据结构笔记一
`
前言
从头复习一下算法和数据结构,并简单的记录下代码,看的网课是b站的左程云的。如有错误请指正
一、排序
1.交换数组中两个位置的数
public void swap(int[] arr, int i, int j){//数组中两数交换位置
arr[i]=arr[i]^arr[j];
arr[j]=arr[i]^arr[j];
arr[i]=arr[i]^arr[j];
}
原理也就是异或运算,需要特别注意因为异或运算的特性,如果参数i位置上的数和参数j位置上的数相等的话,那么这两个位置上的数会变成0
2.随机整型数组生成
为了自己产生数据来检查所写的代码的正确性
public int[] RandomArray(int min,int max,int num){//随机数组生成,min和max是产生的数组中的最大最小边界,num是产生的数组的长度
int[]arr=new int[num];
for(int i=0;i<num;i++){
arr[i]=(int)(Math.random()*(max-min)+min);
}
return arr;
}
3.选择排序及测试代码
原理就是在i位置将数组第i小的数交换到i位置上,i的值从0出发,最终完成整个数组的排序
public static void SelectSort(int[] arr){
if(arr==null||arr.length<2) return ;//空的和只有一个数的数组不用排序
for(int i=0;i<arr.length-1;i++){
int MinIndex=i;//取一个临时变量,存储要交换的值的下标,一开始指向数组的开头
for(int j=i+1;j<arr.length;j++){
MinIndex=arr[j]<arr[MinIndex]?j:MinIndex;
}
if(MinIndex!=i)swap(arr,i,MinIndex);
}
}
public static void main(String[] args) {
int min=0,max=100;
int num=100;
for(int j=0;j<10000;j++) {//这里设置这样的比较需要多少次,防止随机时间的发生建议设置大一些
int[] a = RandomArray(min, max, num);//创建随机数组
int[] b = a.clone();
SelectSort(a);//使用自己的排序
Arrays.sort(b);//系统的排序做比较
for (int i = 0; i < num; i++) {
if (a[i] != b[i]) System.out.print("false" + "\n");
}
}
}
每次生成一个随机的数组,将自己的排序结果和Java自带的排序的结果相比较,这样的比较循环10000次防止偶然事件的发生。
4.冒泡排序
public static void BubbleSort(int[] arr){
if(arr==null||arr.length<2) return ;//空的和只有一个数的数组不用排序
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){//这里注意j<arr.length-i-1是因为j和j+1比较和交换为了防止数组越界所以要减一
if(arr[j]>arr[j+1])swap(arr,j,j+1);
}
}
}
5.异或运算的两小题目
1.已知一个数组中只有一个数出现了奇数次,其他数都出现了偶数次,请问怎么找出这个出现了奇数次的数:
解法:将这个数组从头到尾异或一遍,N^N=0,并且0异或N等于N,那么所有出现偶数次的数会自己消去,出现了奇数次的数最终会留下。
2.已知一个数组中只有两个数出现了奇数次,其他数都出现了偶数次,请问怎么找出这两个出现了奇数次的数:
解法:和第一题一样将这个数组从头到尾异或一遍,那么最后剩下的就是这两个奇数异或的结果即A^B,由题目知A不等于B那么异或的结果不为0,换言之异或的结果的二进制表示中,一定有一位不为0,那么我们假设是第6位,也就是说A和B,第六位不相同,且一个为1一个为0,我们从头到尾把所有第六位为0的数都异或一遍,那么剩下的数一定是A和B之中的一个,接下来要做的就是把第一次和第二次的结果再异或一次,这样两个数就都得到了。
6.插入排序
原理就是像理扑克牌一样,让0-1,0-2,0-3……上的数有序,最终整个数组有序
public static void InsetionSort(int[] arr){//插入排序
if(arr==null||arr.length<2) return ;//空的和只有一个数的数组不用排序
for(int i=1;i<arr.length;i++){//使数组在0到i上有序
for(int j=i;j>0;j--){
if(arr[j]<arr[j-1])swap(arr,j,j-1);
}
}
}
7.归并排序
原理:整个排序分为两个过程,1.是递归的拆分(可以二分也可以不二分),将整个数组拆分为两个部分,将拆分之后两个部分再分别拆分……当拆到只有一个数时开始调用第二个过程,2.归并,将两个部分的数按照顺序归并到一个数组里面,这里就是简单介绍代码如下:
public static void MergeSort(int[] arr,int L,int R){
if(arr==null||L==R) return ;//空的和只有一个数的部分不用排序
int mid=L+((R-L)>>1);
MergeSort(arr,L,mid);
MergeSort(arr,mid+1,R);
merge(arr,L,mid,R);
}
public static void merge(int[]arr,int L,int mid,int R){
if(L==R)return;
int []help=new int[R-L+1];
int i=0;
int p1=L;//左指针
int p2=mid+1;//右指针
while (p1<=mid&&p2<=R){
help[i++]=arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=R){
help[i++]=arr[p2++];
}
for(int j=0;j<help.length;j++){
arr[L+j]=help[j];
}
}
归并排序的扩展
小和问题和逆序对问题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
例子:[1,3, 4,2,5] 1左边比1小的数没有 ; 3左边比3小的数1 ; 4左边比4小的数1、3 ; 2左边比2小的数1 ; 5左边比5小的数1、 3、4、2;所以小和为1+1+3+1+1+3+4+2=16
这道题的解法只需要修改归并排序的代码即可,因为归并排序的过程中实际是二分检查了每一个数,当对这个数求小和时只需要在归并时将这个数左边部分的符合要求的数加起来。(怎么判断这个数左边是哪些数呢?在merge过程时会把左右两半的数组合并,在这个时候一边比较一边计算小和)代码如下
public static int MergeSort1(int[] arr,int L,int R){
if(arr==null||L==R) return 0;//空的和只有一个数的部分不用排序
int mid=L+((R-L)>>1);
//这里对原理再次说明,当运行到这一步时是在对数组进一步二分,也就是说天然的将数组分为了[0,mid]和[mid+1,n]
//每个数都能成为mid+1,除了arr[0],当然arr[0]也不需要对他求小和
return MergeSort1(arr,L,mid)+MergeSort1(arr,mid+1,R)+merge1(arr,L,mid,R);
}
public static int merge1(int[]arr,int L,int mid,int R){
if(L==R) return 0;
int []help=new int[R-L+1];
int tem=0;//计数
int i=0;
int p1=L;//左指针
int p2=mid+1;//右指针
while (p1<=mid&&p2<=R){
if(arr[p1]<arr[p2]){
tem=tem+((R-p2+1)*arr[p1]);//比左边数组指针所指的数大的数有几个
help[i++]=arr[p1++];
}
else help[i++]=arr[p2++];
}
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=R){//当左侧的数组已经全部排序了,也就是说右侧数组即使剩下数也不需要再去计算小和了
help[i++]=arr[p2++];
}
for(int j=0;j<help.length;j++){
arr[L+j]=help[j];
}
return tem;
}
总结
这是第一篇,内容上还没有结束,但是篇幅太长的话不好读,所以到这里结束