备战Java后端【Day7】
数据结构1-2
数组2
- 左右指针的常用算法:二分查找
- 左右指针的常用算法:两数之和
- 左右指针的常用算法:反转数组
- 左右指针的常用算法:回文串判断
学习目标
- 数组基础知识复习2(数组基本操作和排序算法)
- 左右指针的常用算法,解决例题
学习内容
数组基础知识
1 数组的基本操作
- 1. 遍历数组
数组遍历就是获取数组中的每个元素。通常遍历数组都是用for循环来实现。遍历二维数组需要使用双层for循环,通过数组的 length 属性可获得数组的长度。
public class shuzu {
public static void main(String[] args) {
int a[][]=new int[3][4];//二维数组
for(int i=0;i<a.length;i++) {
for(int j=0;j<a[i].length;j++) {
System.out.print(a[i][j]);
}
System.out.println();
}
}
}
在遍历数组时,使用 foreach 语句会更简单。
public class Tautog{ //创建类
public static void main(String[] args){ //主方法
int arr2[][]={{4,3},{1,2}}; //定义二维数组
System.out.println("数组中的元素是:"); //提示信息
int k=0; //外层循环计数器变量
for(int x[]:arr2) { //外层循环变量为一维数组
k++; //外层循环计数器递增
int j=0; //内层循环计数器
for(int e:x) { //循环遍历每一个数组元素
j++; //内存计数器递增
if(k==arr2.length&&j==x.length) { //判断变量是二维数组中的最后一个元素
System.out.println(e); //输出二维数组的最后一个元素
}
else //如果不是二维数组中的最后一个元素
System.out.print(e+"、"); //输出信息
}
}
}
}
- 2. 填充替换数组元素
数组中的元素定义完成后,可通过Arrays类的静态方法 fill( ) 来对数组中的元素进行替换。该方法通过各种重载的形式可完成对任意类型的数组元素的替换。
- fill(int[ ] a, int value)
该方法可以将指定的 int 值分配给 int 型数组的每个元素。其中:
① a: 表示要进行元素替换的数组。
② value: 要存储数组中所有原色的值。
import java.util.Arrays;
public class Swap{
public static void main(String[] args){
int arr[]=new int[5];
Arrays.fill(A, 8);//整个数组全部填充
System.out.println("全部填充后的数组元素为:");
for(int i=0;i<5;i++){
System.out.print(A[i]+" ");
}
}
}
- fill(int[ ] a, int fromIndex, int toIndex, int value)
① a: 表示要进行元素替换的数组。
② value: 要存储数组中所有原色的值。
③ int fromIndex: 要使用指定值填充的第一个元素的索引(包括)
④ int toIndex: 要使用指定值填充的最后一个元素的索引(不包括)
import java.util.Arrays;
public class Displace{
public static void main(String[] args){
int arr[]=new int[]{45,12,2,10};
Arrays.fill(arr, 1, 2, 8);//填充结果包括起始的第一个,不包括结尾的最后一个
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
}
- 3. 数组排序
通过Arrays类的静态 sort() 方法可以实现对数组的排序。sort() 方法提供了多种重载形式,可对任意类型的数组进行升序排序。语法如下:
Arrays.sort(object)
在这里,利用 Arrays.sort(arr,Collections.reverseOrder()) 可以实现逆序排序。
import java.util.Arrays;
public class Displace{
public static void main(String[] args){
int arr[]=new int[] {1,8,3,2,5,8,9,3,5,0};
Arrays.sort(arr);//升序排列
for(int i=0;i<10;i++){
System.out.print(arr[i]+" ");
}
Integer[] arr3= {1,2,3,4,5,6,7,8,9};
System.out.println();
System.out.println("降序后的数组为元素为:");
Arrays.sort(arr3,Collections.reverseOrder());//降序排列
for(int i:arr3){
System.out.print(i+" ");
}
}
}
- 4. 数组复制
Arrays类的 copyOf( ) 方法与 copyOfRange( ) 方法可以实现对数组的复制。copyOf( ) 方法是复制数组至指定长度,copyOfRange( ) 方法则将指定数组的指定长度复制到一个新数组中。
- copyOf( ) 方法
copyOf(arr,int newlength)
newlength 指的是复制后新数组的长度
- copyOfRange( ) 方法
copyOfRange(arr,int fromIndex, int toIndex)
import java.util.Arrays;
public class Displace{
public static void main(String[] args){
System.out.println();
System.out.println("复制后的数组为元素为:");//全局复制
int newarr1[]=Arrays.copyOf(arr, 15);
for(int i:newarr1){
System.out.print(i+" ");
}
System.out.println();
System.out.println("部分复制后的数组为元素为:");//部分复制
int newarr2[]=Arrays.copyOfRange(arr,0,5);
for(int i:newarr2){
System.out.print(i+" ");
}
}
}
- 5. 数组查询
Arrays类的 binarySearch( ) 方法,可使用二分搜索法来搜索指定数组,以获得指定对象。该方法返回要搜索元素的索引值。binarySearch( ) 方法提供了多种重载形式,用于满足各种类型数组的查找需要。binarySearch( ) 方法有两种参数类型:
① binarySearch(Object[ ] a,Object key )
② binarySearch(Object[ ] a, int fromIndex, int toIndex, Object key )
其中 Object key为要搜索的元素。
import java.util.Arrays;
public class Displace{
public static void main(String[] args){
int arr[]=new int[] {1,8,3,2,5,8,9,3,5,0};
int index1=Arrays.binarySearch(arr, 2);//全局查询
System.out.println("arr中元素2的索引位置为:"+index1);
int index2=Arrays.binarySearch(arr,0,3,2);//索引位置0到3的范围查询元素3的索引位置
System.out.println("arr中第1到4个元素中元素3的索引位置为:"+index2);
}
}
2 数组排序算法
- 1. 冒泡排序法
- 基本思想
冒泡排序的基本思想是对比相邻的元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置),这样较小的元素就像气泡一样从底部上升到顶部。 - 算法示例
冒泡算法由双层循环实现,其中外层循环用于控制排序轮数,一般为要排序的数组长度减1次,因为最后一次循环只剩下一个数组元素,不需要对比,同时数组已经完成排序了。而内层循环主要用于对比数组中每个邻近元素的大小,以确定是否交换位置,对比和交换次数随排序轮数而减少。例如,一个拥有6个元素的数组,在排序过程中每一次循环的排序过程和结果如下图所示:
第一轮外层循环时把最大元素值63移动到了最后面(相应地,比63小的元素向前移动,类似气泡上升),第二轮外层循环不再对比最后一个元素值63,因为它已经被确认为最大(不需要上升),应该放在最后,需要对比和移动的是其他剩余元素,这次将元素24移动到了63的前一个位置上。其他循环将以此类推,继续完成排序任务。 - 算法实现
import java.util.Arrays;
import java.util.Collections;
public class MaoPaopx {
public static void main(String[] args) {
Integer[] array= {63,4,24,1,3,15};
// 创建一个数组,这个数组的元素是乱序的
MaoPaopx sorter=new MaoPaopx();
// 创建冒泡排序类的对象
sorter.sort(array);
//调用排序方法将数组排序
}
// 冒泡排序
public void sort(Integer[] array) {
for(int i=1;i<array.length;i++) {//比较相邻两个元素,较大的数往后冒泡
for(int j=0;j<array.length-i;j++) {
if(array[j]>array[j+1]) {
int temp=array[j];//把第一个元素值保存到临时变量中
array[j]=array[j+1];//把第二个元素保存到第一个元素单元中
array[j+1]=temp;//把临时变量(也就是第一个元素值)保存到第二个元素中
}
}
}
showArray(array);//输出冒泡排序后的数组元素(顺序)
showshowArray(array);//输出冒泡排序后的数组元素(倒序)
}
//显示数组中的所有元素(顺序)
public void showArray(Integer[] array) {
System.out.print("冒泡排序结果为(顺序):");
for(int i:array) {
System.out.print(i+" ");
}
System.out.println();
}
//显示数组中的所有元素(倒序)
public void showshowArray(Integer[] array) {
System.out.print("冒泡排序结果为(倒序):");
Arrays.sort(array,Collections.reverseOrder());//倒排序
for(int i:array) {
System.out.print(i+" ");
}
}
}
- 2. 直接选择排序法
直接选择排序方法属于选择排序的一种,它的排序速度比冒泡排序快一些,也是常用的排序算法。
- 基本思想
直接选择排序的基本思想是将指定排序位置与其他数组元素分别对比,如果满足条件就交换元素值。注意这里与冒泡排序的区别,不是交换相邻元素,而是把满足条件的元素与指定的排序位置交换(如从最后一个元 素开始排序),这样排序好的位置逐渐扩大,最后整个数组都成为已排序好的格式。
这就好比有一个小学生, 从包含数字1~10的乱序的数字堆中分别选择合适的数字,组成一个1至10的排序,而这个学生首先从数字堆中选出1,放在第一位,然后选出2 (注意这时数字堆中已经没有1了),放在第二位,依此类推,直到其找到数字9,放到8的后面,最后剩下10,就不用选择了,直接放到最后就可以了。
与冒泡排序相比,直接选择排序的交换次数要少很多,所以速度会快一些。 - 算法示例
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序地放在已排好地数列地最后,直到全部待排序的数据元素排完。
①初始数据资源 【63 4 24 1 3 15】
②第一趟排序后 【15 4 24 1 3】63
③第二趟排序后 【15 4 3 1】24 63
③第三趟排序后 【1 4 3】15 24 63
④第四趟排序后 【1 3】4 15 24 63
⑤第五趟排序后 【1】3 4 15 24 63 - 算法实现
import java.util.Arrays;
import java.util.Collections;
public class SelectSort {
public static void main(String[] args) {
Integer[] array= {63,4,24,1,3,15};
SelectSort sorter=new SelectSort();
sorter.sort(array);//调用排序对象的方法将数组排序
}
//直接选择排序
public void sort(Integer[] array) {
int index;
for(int i=1;i<array.length;i++) {
index=0;
for(int j=1;j<=array.length-i;j++) {
if(array[j]>array[index]) {
index=j;
}
}
int temp=array[array.length-i];
array[array.length-i]=array[index];
array[index]=temp;
}
showArray(array);
showshowArray(array);
}
//显示数组中所有元素(顺序)
public void showArray(Integer[] array) {
System.out.print("ֱ直接选择排序(顺序)输出为:");
for(int i:array) {
System.out.print(i+" ");
}
System.out.println();
}
//显示数组中的所有元素(倒序)
public void showshowArray(Integer[] array) {
System.out.print("ֱ直接选择排序(倒序)输出为:");
Arrays.sort(array,Collections.reverseOrder());//元素顺序翻转
for(int i:array) {
System.out.print(i+" ");
}
}
}
- 3. 反转排序法
顾名思义,反转排序就是以相反的顺序把原有数组的内容重新排序。
- 基本思想
反转排序的基本思想比较简答,其实现思路就是把数组最后一个元素与第一个元素替换,倒数第二个元素与第二个元素替换,以此类推,直到把所有元素反转替换。 - 算法示例
反转排序是对数组两边的元素进行替换,所以只需要循环数组长度的半数次,如数组长度为7,那么for循环只需要循环3次。
①初始数据资源 【10 20 30 40 50 60】
②第一趟排序后 60【20 30 40 50 10】
③第二趟排序后 60 50【30 40】20 10
④第三趟排序后 60 50 40 30 20 10 - 算法实现
public class ReverseSort {
public static void main(String[] args) {
int[] array= {10,20,30,40,50,60};
ReverseSort sorter=new ReverseSort();
sorter.sort(array);
// 调用排序对象的方法,将数组反转
}
//反转排序
public void sort(int[] array) {
System.out.println("数组原有内容:");
showArray(array);
int temp;
for(int i=0;i<array.length/2;i++) {
temp=array[i];
array[i]=array[array.length-i-1];
array[array.length-i-1]=temp;
}
System.out.println("数据反转后的内容:");
showArray(array);//输出排序后的数组元素
}
//显示数组中的所有元素
public void showArray(int[] array) {
for(int i:array) {
System.out.print("\t"+i);//输出每个数组元素的值
}
System.out.println();
}
}
学习时间
2022年7月15日
- 下午2点-下午5点
- 晚上7点-晚上9点
学习产出
- 本文档链表基础知识和基础操作
- 力扣5题,最长回文子串
- 力扣83题,删除排序链表中的重复元素
- 力扣167题,两数之和Ⅱ-输入有序数组
- 力扣283题,移动零
- 力扣344题,反转字符串
今日刷题
5.最长回文子串
对于有序的数组问题,首先应该想到左右指针,寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串。找回文串的关键技巧是传入两个指针 l 和 r 向两边扩散,因为这样实现可以同时处理回文串长度为奇数和偶数的情况。
class Solution {
public String longestPalindrome(String s) {
String res = "";
for (int i = 0; i < s.length(); i++) {
// 以 s[i] 为中心的最长回文子串
String s1 = palindrome(s, i, i);
// 以 s[i] 和 s[i+1] 为中心的最长回文子串
String s2 = palindrome(s, i, i + 1);
// res = longest(res, s1, s2)
res = res.length() > s1.length() ? res : s1;
/*
*这是一个三元运算符,表达的意思为:
*若 res.length()>s1.length() 则 res = res
*否则 res = s1
*可以用 if...else...语句替代
*/
res = res.length() > s2.length() ? res : s2;
}
return res;
}
String palindrome(String s, int l, int r) {
// 防止索引越界
while (l >= 0 && r < s.length()
&& s.charAt(l) == s.charAt(r)) {
// 向两边展开
l--;
r++;
}
// 返回以 s[l] 和 s[r] 为中心的最长回文串
return s.substring(l + 1, r);
}
}
83. 删除排序链表中的重复元素
思路和 26. 删除有序数组中的重复项 完全一样,唯一的区别是把数组赋值操作变成操作指针而已。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null) return null;
ListNode slow = head, fast = head;
while (fast != null) {
if (fast.val != slow.val) {
// nums[slow] = nums[fast];
slow.next = fast;
// slow++;
slow = slow.next;
}
// fast++
fast = fast.next;
}
// 断开与后面重复元素的连接
slow.next = null;
return head;
}
}
167. 两数之和Ⅱ-输入有序数组
在这里,先来看下最简单的二分查找算法,体现了其双指针的特性。
int binarySearch(int[] nums, int target) {
// 一左一右两个指针相向而行
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
return -1;
}
只要数组有序,就应该想到双指针技巧。这道题的解法有点类似二分查找,通过调节 left 和 right 就可以调整 sum 的大小:
class Solution {
public int[] twoSum(int[] nums, int target) {
// 一左一右两个指针相向而行
int left = 0, right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
// 题目要求的索引是从 1 开始的
return new int[]{left + 1, right + 1};
} else if (sum < target) {
left++; // 让 sum 大一点
} else if (sum > target) {
right--; // 让 sum 小一点
}
}
return new int[]{-1, -1};
}
}
283. 移动零
可以直接复用 27. 移除元素 的解法,先移除所有 0,然后把最后的元素都置为 0,就相当于移动 0 的效果。
class Solution {
public void moveZeroes(int[] nums) {
// 去除 nums 中的所有 0
// 返回去除 0 之后的数组长度
int p = removeElement(nums, 0);
// 将 p 之后的所有元素赋值为 0
for (; p < nums.length; p++) {
nums[p] = 0;
}
}
// 双指针技巧,复用 [27. 移除元素] 的解法。
int removeElement(int[] nums, int val) {
int fast = 0, slow = 0;
while (fast < nums.length) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
}
344. 反转字符串
可以直接使用 reverse 函数来进行,同时使用左右指针技巧。
class Solution {
public void reverseString(char[] s) {
// 一左一右两个指针相向而行
int left = 0, right = s.length - 1;
while (left < right) {
// 交换 s[left] 和 s[right]
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}