本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为“云同桌”。
每章都会从整体框架入手,介绍章节所涉及的重要知识点及相关练习题,并会设置推荐学习时间,每篇博客涉及到的点都会在开篇目录进行总览。(博客中所有高亮部分表示是面试题进阶考点)
Java简单输入与数组
学习时间:一周
学习建议:数组是学习集合之前主要的数据存储结构,经常使用,希望能够深入理解,并掌握相关的一些排序算法
1.Scanner类简单用户输入
java中接受用户输入通常使用scanner对象
声明scanner对象:
Scanner scanner = new Scanner(System.in);
调用scanner对象的一些方法可以接收用户输入,各方法分别对应不同的基础数据类型,表格左边为方法的返回值类型
注意:
scanner.next(),接收用户输入字符串时,会过滤掉有效字符前的无效字符(空格、tab、回车),读取到有效字符后,下一个无效字符视为结束
如:“ abc cdf”只能接收为“abc”
scanner.nextLine()接收的是回车键之前所有的字符串,空格不影响
有时候因为交付标准,不能因为用户输入错误而导致程序崩溃,但有时候必须接受了该类型的数据才能进行某段代码
因此,可以在正式接收数据之前即scanner.next()之前进行判断是什么类型,通过if条件进行限制
scanner.hasNextInt()返回一个布尔型数据,可以作为if的判断条件,限制用户输入的数据类型进入此作用域
if(scanner.hasNextInt()){
//只有用户输入的数据为int才能执行此代码
}
同理,还有scanner.hasNextFloat(),只有float数据才返回true,等这样对应基础数据类型的方法。
2.一维数组
(1)基本知识
数组:是相同数据类型的多个数据的容器。这些元素按线性顺序排列。所谓线性顺序是指除第一个元素外,每一个元素都有唯一的前驱元素;除最后一个元素外,每一个元素都有唯一的后继元素。
创建数组的方式有:
(1)int[] num = new int[20];//常用
(2)int[] num = {1,2,3,4,5};//常用
(3)int[] num; //只创建了数组引用,未创建数组空间
(4)int[] num = new int[]{1,2,3,4,5};//不常用
操作数组中的值是通过数组的下标进行操作,下标由0到n-1,分别对应数组中n个数的值,数组名.length属性返回数组的长度n(不必纠结是声明长度还是有效长度,java中数组被声明后自动将里面的元素有赋成了该数据类型的默认值了)
对于某些算法题里出现的空数组,例:
int[] num = null;
需要注意,一个数据对应一个下标,空数组没有数据,就没有下标,下标0也会造成越界。唉,一言难尽。
(2)数组常见问题
数组越界:
ArrayIndexOutOfBoundsException
常发生于操作时的下标大于数组长度时候报错
空指针异常:
NullPointerException
数组引用没有指向实体,却在操作实体中的元素,或者说操作的数组下标对应的位置没有值,值为null时报错
(3)数组相关问题——冒泡排序
思想:对当前的数据两两比较,根据升降序决定前后位置,每轮数据两两比较完后,会得到一个最大值(在末尾)或最小值(在开头)对n个数据,进行n-1轮内层的比较循环,内层里将对参与该轮的所有数据进行相邻比较,即可将n个数排序
外层循环:控制n个数据的n-1轮内层循环
内层循环:控制该轮的length-i个数据进行length-i-1次相邻比较
代码模块:
public int[] sort_bubble(int[] arr) {
//冒泡排序
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length - 1 - i;j++){
if(arr[j] > arr[j + 1]){//这里用了异或
// 来不引入第三方变量交换数值
arr[j] = arr[j]^arr[j+1];
arr[j+1] = arr[j+1]^arr[j];
arr[j] = arr[j]^arr[j+1];
}
}
}
return arr;
}
(4)数组相关问题——数组元素反转
反转即将数组中数的顺序按照反序重新存进去
思想:
将下标0和下标length-1的值,交换;
将下标1和下标length-2的值,交换;
将下标2和下标length-3的值,交换;
将下标3和下标length-4的值,交换;
直到走到了中间同一个位置;
代码模块:
public int[] reverse(int[] arr) {
for(int i = 0 ,j = arr.length-1 ;i<j ; i++,j--) {
arr[j] = arr[j]^arr[i];
arr[i] = arr[i]^arr[j];
arr[j] = arr[j]^arr[i];
}
return arr;
}
(5)数组相关问题——二分法查找
二分法查找的前提:有序数组
二分法查找的思想:
将待查数据与检索范围中间下标数据进行比较
大于中间下标数据,则检索范围在中间下标+1 —— 右边界;
小于中间下标数据,则检索范围在中间下标-1 —— 左边界
逐渐缩小检索范围,直到范围边界重合(左边界==右边界)即找到数据,边界错位(左边界>右边界)即数据不存在,不论数据在不在范围,只要没有出现在数组中,min就会一直往右走,必定大于max
代码模块:
public int serch_binnary(int num) {
//待查找的数,num
max = arr.length-1;
min = 0;
while(true){
mid = (max+min)/2;
if(arr[mid]<num) {
min = mid+1;
}else if(arr[mid]>num) {
max = mid -1;
}else {
System.out.println("该数据在数组中的下标为" + mid);
break;
}
if(min>max) {
System.out.println("数据不存在");
break;
}
}
return mid;
}
(6)数组相关问题——快速排序
快速排序作为最基础的排序算法之一,必然是少不了的,在不少公司笔试的时候,最后一道编程大题,很大概率就是快速排序
快速排序的思想:主要利用的还是分治和递归的思想
即一开始先遍历数组,将大于某个数的放在该数的右边,小于某个数的放在该数的左边,这样就以那个数为边界,分成了两个区域,确定了这两个区域的边界后,进行接下来的递归的排序,对这两个区域再执行上面的步骤即可,直到某个小区域的边界重合或左边界>右边界
代码实例:
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
//输入用户数据
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
String[] temp = s.split(",");
int[] str = new int[temp.length];
for(int i = 0;i<temp.length;i++){
str[i] = Integer.parseInt(temp[i]);
}
//调用快排
int begin = 0;
int end =str.length-1;
quick_sort(str,begin,end);
//输出结果
for(int i :str){
System.out.print(i);
}
}
//快排
public static void quick_sort(int[] str,int begin,int end){
if(str.length<=1){
return;
}
//左边界>=右边界排序完毕
if(begin>=end){
return;
}
int l = begin;
int r = end;
//标志方向,定义true向左,false向右
boolean flag = true;
//边界重合,左右区域划分完毕
while(l != r){
if(str[l]>str[r]){
//交换
str[l] = str[l]^str[r];
str[r] = str[r]^str[l];
str[l] = str[l]^str[r];
flag = !flag;
}
if(flag == true){
//向左比较
l++;
}else{
//向右比较
r--;
}
}
//划分完毕子区域,此时l和r指向的即中间边界
//左边区域
quick_sort(str, begin, --l);
quick_sort(str, ++r, end);
}
}
(7)常见排序算法的时间复杂度
3.二维数组
首先应该明白一个思想,任何多维数组(维度为n,n>1)都当作一维数组,其数组元素为n-1维数组
从内存中的存放形式来看,数组引用变量是存放在栈内存(stack)中,数组元素是存放在堆内存(heap)中
数组中存放的大多数是基本数据类型,但也可以存放引用数据类型,当数组元素存放是另一个数组的索引,内存的存放形式便发生了下图的变化
这样可以把这个二维数组抽象理解成为一个矩阵,宽是一维数组的长度,长是二维数组的长度
那么声明二维数组可以如下声明:
int[][] nums = new int[width][length];
//width指定一维数组的长度,必须有
//length指定每个二维数组的长度,可以没有,每个二维数组的长度可以不一致
那么在访问数据元素的也从num[0]到num[num.length-1],
变成了,nums[0][0]到nums[nums.length-1][[nums.length-1].length-1]
或者在已知二维数组元素的情况下,可以直接赋值,如:
int[][] arr = {{1,2}, {2, 3}, {4, 5}};
这样相当于建立的是一个int[3][2]的二维数组
一个{}表示结束了一个一维数组的赋值,该大括号里面就是该一维数组对应的二维数组的元素,如{1,2}表示结束了下标为0的一维数组的赋值,该大括号里面 1和2 就是该下标为0的一维数组对应的二维数组的元素。
上图的存储结构可以看做:
1/*arr[0][0]*/ 2/*arr[0][1]*/ //arr[0]
2/*arr[1][0]*/ 3/*arr[1][1]*/ //arr[1]
4/*arr[2][0]*/ 5/*arr[2][1]*/ //arr[2]
都学习到这里了,不妨关注点赞一下~