2022-7-15 java 数据结构入门
@
数据结构
1.二分查找
动图演示:
前提: 使用二分查找的前提是数组是有序的
int []arr ={1,2,3,4,5,6,7};
Scanner scanner = new Scanner(System.in);
int left = 0;//最左边元素的下标
int right = arr.length-1;//最右边元素的下标
int mid = 0;//中间元素的值
System.out.println("请输入一个数:");
int k = scanner.nextInt();//要查找的元素k
while (left <= right){
mid = (left+right)/2;
if (arr[mid]>k){
right = mid-1;
}
else if (arr[mid]<k){
left = mid+1;
}
else {
break;
}
}
if (left>right){
System.out.println("没有找到该数");
}else {
System.out.println("找到了");
}
2.冒泡排序
1.冒泡排序原理
2.冒泡排序基础版
//按照刚才那个动图进行对应
//冒泡排序两两比较的元素是没有被排序过的元素--->
public void bubbleSort(int[] array){
for(int i=0;i<array.length-1;i++){//控制比较轮次,一共 n-1 趟
for(int j=0;j<array.length-1-i;j++){//控制两个挨着的元素进行比较
if(array[j] > array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
3.冒泡排序代码优化版
说明:因为在最后几轮,数组可能已经是有序的了,没有必要再进行循环排序了
- 利用布尔变量 isSorted作为标记。如果在本轮排序中,元素有交换,则说明数列无序;如果没有元素交换,说明数列已然有序,直接跳出大循环
public static int[] bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
for (int i = 0; i < arr.length - 1; i++) {
boolean isSorted = true;//有序标记,每一轮的初始是true
for (int j = 0; j < arr.length -i - 1; j++) {
if (arr[j + 1] < arr[j]) {
isSorted = false;//有元素交换,所以不是有序,标记变为false
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
}
}
//一趟下来是否发生位置交换,如果没有交换直接跳出大循环
if(isSorted )
break;
}
return arr;
}
4.冒泡排序代码升级版
如果数列中前半部分是无序的,后半部分是有序的呢?比如(3,4,2,1,5,6,7,8)这个数组,其实后面的许多元素已经是有序的了,但是每一轮还是白白比较了许多次
- 解决方法:在每一轮排序的最后,记录一下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区
public static int[] bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return arr;
}
//记录最后一次交换的位置
int lastExchangeIndex = 0;
//无序数列的边界,每次比较只需要比到这里为止
int sortBorder = arr.length - 1;
for (int i = 0; i < arr.length - 1; i++) {
boolean isSorted = true;//有序标记,每一轮的初始是true
for (int j = 0; j < sortBorder; j++) {
if (arr[j + 1] < arr[j]) {
isSorted = false;//有元素交换,所以不是有序,标记变为false
int t = arr[j];
arr[j] = arr[j+1];
arr[j+1] = t;
lastExchangeIndex = j;
}
}
sortBorder = lastExchangeIndex
//一趟下来是否发生位置交换,如果没有交换直接跳出大循环
if(isSorted )
break;
}
return arr;
}
3.选择排序
1. 算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
2.选择排序图解:
3.代码
public static void searchSort(int[] arr){
for (int i = 0; i<arr.length - 1; i++){//①
int minIndex = i;
int min = arr[i];//②
for (int j = i + 1; j<arr.length; j++){//③
if(min>arr[j]){
min = arr[j];
minIndex = j;
}
}
arr[minIndex] = arr[i];
arr[i] = min;//④
}
System.out.println(Arrays.toString(arr));
}
①.我们定义一个循环,这个循环就是用来进行上述的每轮排序行为的,在这个循环中,每轮循环都会找到当前数组中的最小值,并让这个最小值和当前数组的首元素进行位置互换。与此同时,循环头中的变量i在这里也有有着丰富的含义,它不仅代表循环的次数,它也代表着每次轮循环中数组的首元素位置,当数组中的首元素位置就是原最大的那个数组中最后一个元素的位置时,就说明排序已经到头,算法可以结束了。
②.我们定义最小值的下标变量,同时定义最小值。这里主要是找最值的方法,找最值的方法一般都是默认数组首节点是最值点,然后对数组进行遍历,在遍历过程中会依次查看整个数组中的所有元素并和最值进行对比,如果发现了比最值小的元素,那么就让当前的数组元素替换掉之前的最值元素成为新的最值元素,如这里使用minIndex保存最值下标,使用min保存最值,当找到数组中新的最值时,新的最值下标就会替换掉minIndex中原来的值,而新的最值也会替换掉min中原来的值。
③.这个循环就是找最值的循环体,具体解释看上文中②的解释。
④.这里的两句代码就是将找到的最值与数组首元素的值进行对换的行为。之后将开启新的一轮循环,新数组将从i开始,而i这时加了1,这就代表着新数组往后“缩”了一格,规模变小了一些。重复进行外循环,直到外循环终止,这个数组最终会被排好序。
4.插入排序
1.插入排序图解
2.算法总结
根据图解,我们现在可以理解插入排序的过程了(以从小到大排序为例):我们将原数组空间看成两个部分,前边是有序部分,后边是无序部分,有序部分我们默认为它就已经是排好序的,它内部已经是从小到大有序的状态了,即使当前它是空的,它也具备这个特征。然后我们不断的取无序数组的首元素,向有序数组的尾部添加,我们在这里并没有真正的将它取出,并且再添加回来,因为根据我们的定义,有序数组的尾部正好就和无序数组的头部相邻,这只是一个更改标记的事情,我们只要把有序数组的长度加1,无序数组的长度减一,并且将有序数组的头部元素指针指向它的下一个,就可以完全准确的代表这个行为。因此这时我们已经将无序数组的首元素取出,并插入在了有序数组的尾部,然而这时,在尾部新加入的元素有可能会导致整个有序数组变得无序,因此我们需要进行调整。而调整方式就是将新加入的元素进行对比并根据对比结果往前移动,这个移动过程有点像冒泡排序的过程:新加入元素和它前边的元素进行对比,如果它比它前边的元素小,则二者互换位置,重复这个行为,直到它前边的元素小于它才会停止,这样一来,有序数组就仍然是有序数组了。我们重复这个向有序数组中插入不断插入数据,并在插入之后通过操作使其变得有序的行为,在这个过程中我们会一个一个的拿走无序数组中的首节点并一个一个单位的减少无序数组的规模,并一个一个的向有序数组中添加并扩大有序数组的规模,直到最后整个无需数组被掏空,有序数组占据了原数组空间的所有空间时,整个数组排序宣告完成。
3.代码
public static void insertSort(int[] arr){
for (int i = 1; i<arr.length; i++){//①
for (int j = i-1; j>=0; j--){//②
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}else
break;
}
}
System.out.println(Arrays.toString(arr));
}
4.代码详解
①标记了外部的大循环,这个循环实际上就是驱动有序数组一个一个增加,无序数组一个一个减少的动力,在此,i就代表了无序数组的首节点位置,因为当数组中有一个元素时它必定有序,所以我们在这里可以忽略掉往插入第一个元素的行为,直接从第二个开始插入。
②标记了外部循环中的一个内循环,外循环的每次执行,实际上都代表了我们将无序数组中的首元素拿下来放到有序数组的尾部,而这里这个内循环,实际上就是移动这个新来的元素,使有序数组重新变得有序的机制,我们可以看到,j指向的是i-1,这个位置就是当前有序数组没有插入新元素之前的尾部位置,在这个循环中,j要一直和j+1进行对比,j+1指向的就是新加入的元素,如果j指向的元素大于j+1指向的元素的话,二者就要进行一个类似冒泡排序的互换,将j+1指向的元素和j指向的元素进行互换,在互换之后,j指向了新加入的元素,而j+1则指向了之前新元素前边的元素,也就是之前的数组尾部,这时j自减一次,这就导致j+1又重新指向了新加入的元素,而j也又指向了新加入的元素的前一个元素,新元素得以继续同它前边的元素进行对比,当经过对比发现,新加入的元素已经大于前边的元素之后,说明新加入的元素已经移动到了自己正确的位置,这个移动行为就可以终止了。
插入排序的精髓在于设置一个有序的空数组,从它为空的时候就认定其为有序数组,并在不断的插入新元素的过程中维护其有序性,这样一来,我们实际上一直在干的事情就是向一个有序数组中不断地插入新元素,这比进行直接排序的操作手法会简单的多。以上就是插入排序。
5.数组反转
// 创建一个字符串的数组
String[] str = new String[]{"AA","BB","CC","DD","EE","FF","GG"};
方式一
临时变量
for(int i = 0; i < str.length / 2; i++){
String temp = str[i];
str[i] = str[str.length - i - 1];
str[str.length - i - 1] = temp;
}
方式二
创建一个新的数组,倒序遍历旧数组中的元素,然后按照顺序复制给新数组,然后再把新数组的值复制到旧数组当中
for(int i = 0,j = str.length - 1; i < j; i++,j--){
// 交换
String temp = str[i];
str[i] = str[j];
str[j] = temp;
}
方式三
创建一个新数组,长度是旧数组的长度。将旧数组倒序遍历获取数组中的元素,拿到元素之后从头开始赋值给新数组当中,最后将新数组的地址值赋值给旧数组
// 方式1.3
String[] newArr = new String[str.length]; // 创建新数组,数组的长度是旧数组的长度
for (int i = str.length - 1, j = 0; i >= 0; i--, j++) {
newArr[j] = str[i];
}
// 循环完毕之后,切记要将newArr数组的地址值赋值给str数组
str = newArr;
// 遍历str数组,查看是否反转了
for(int i = 0; i < str.length; i++){
System.out.print(str[i] + " ");
}