java初级编程
多维数组的使用
练习1
练习1
/*
* 或许arr数组中所有元素的和
* 使用for的嵌套循环
*/
public class ArrayExer1 {
public static void main(String[] args) {
int[][] arr = new int[][]{{3,5,8},{12,9},{7,0,6,4}};
int sum = 0;//记录总和
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr[i].length;j++){
sum += arr[i][j];
}
}
System.out.println("总和为:" + sum);
}
}
练习2
==int[] x,y[]==
- x定义为一维数组;y是二维数组;int[]x;int[]y[]
+ 一维数组赋值:int[] x 或者int x[]
+ 二维数组赋值:int[][]y 或 者 int[] y[] 或者 int y[][]
//以下是否能通过编译
+ x[0] = y; no
+ y[0] = x; yes
+ y[0][0] = x; no
+ x[0][0] = y; no
+ y[0][0] = x[0]; yes
+ x = y; no
练习3
- 使用二维数组打印一个10行杨辉三角
- 第一行有1个元素,第n行有n个元素
- 每一行的第一个元素和最后一个元素都是1
- 从第三行开始,对于非第一个元素和最后一个元素的元素。即:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
/*
* 使用二维数组打印一个 10 行杨辉三角。
【提示】
1. 第一行有 1 个元素, 第 n 行有 n 个元素
2. 每一行的第一个元素和最后一个元素都是 1
3. 从第三行开始, 对于非第一个元素和最后一个元素的元素。即:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
*
*/
public class YangHuiTest {
public static void main(String[] args) {
//1.声明并初始化二维数组
int[][] yangHui = new int[10][];
//2.给数组的元素赋值
for(int i = 0;i < yangHui.length;i++){
yangHui[i] = new int[i + 1];
//2.1 给首末元素赋值
yangHui[i][0] = yangHui[i][i] = 1;
//2.2 给每行的非首末元素赋值
//if(i > 1){
for(int j = 1;j < yangHui[i].length - 1;j++){
yangHui[i][j] = yangHui[i-1][j-1] + yangHui[i-1][j];
}
//}
}
//3.遍历二维数组
for(int i = 0;i < yangHui.length;i++){
for(int j = 0;j < yangHui[i].length;j++){
System.out.print(yangHui[i][j] + " ");
}
System.out.println();
}
}
}
数组中涉及到的常见的算法题
- 数组元素的赋值(杨辉三角、回形数等)
- 求数值型数组中元素的最大值、最小值、平均数、总和等
- 数组的复制、反转、查找(线性查找、二分法)
- 数组元素的排序算法
练习一
创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。
public class ArrayExer3 {
public static void main(String[] args){
int[] arr = new int[6];
for (int i = 0; i < arr.length; i++){
// 公式:Math.random()*(n-m)+m,生成大于等于m小于n的随机数;
arr[i] = (int)(Math.random()*30 + 1);
boolean judgement = true;
while(true){
for (int j = 0;j < i;j++){
if (arr[j] == arr[i]){
judgement = true;
break;
}
}
if (judgement){
arr[i] = (int)(Math.random()*30 + 1);
judgement = false;
continue;
}
break;
}
}
for (int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}
//厉害方法
int[] arr = new int[6];
for (int i = 0; i < arr.length; i++) {// [0,1) [0,30) [1,31)
arr[i] = (int) (Math.random() * 30) + 1;
for (int j = 0; j < i; j++) {
if (arr[i] == arr[j]) {
i--;
break;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
练习二
/*
* 回形数格式方阵的实现
从键盘输入一个整数(1~20)
则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。例如: 输入数字2,则程序输出: 1 2
4 3
输入数字3,则程序输出: 1 2 3
8 9 4
7 6 5
输入数字4, 则程序输出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
*/
import java.util.Scanner;
public class ArrayExer4 {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int len = scanner.nextInt();
int[][] arr = new int[len][len];
int s = len * len;
int k = 1;
int i = 0, j = 0;
for(int m = 1;m < s;m++){
if (k == 1){
if (j < len && arr[i][j] == 0){
arr[i][j++] = m;
}else{
k = 2;
i++;
j--;
m--;
}
}else if (k == 2){
if (i < len && arr[i][j] == 0){
arr[i++][j] = m;
}else{
k = 3;
i--;
j--;
m--;
}
}else if (k == 3){
if (j >= 0 && arr[i][j] == 0){
arr[i][j--] = m;
}else{
k = 4;
i--;
j++;
m--;
}
}else if (k == 4){
if (i >= 0 && arr[i][j] == 0){
arr[i--][j] = m;
}else{
k = 1;
i++;
j++;
m--;
}
}
}
for (int m = 0; m < arr.length;m++){
for(int n = 0;n < arr[m].length;n++){
System.out.print(arr[m][n] + "\t");
}
System.out.println();
}
}
}
练习三
/*
* 算法的考查:求数值型数组中元素的最大值、最小值、平均数、总和等
*
* 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,
* 然后求出所有元素的最大值,最小值,和值,平均值,并输出出来。
* 要求:所有随机数都是两位数。
*
* [10,99]
* 公式:(int)(Math.random() * (99 - 10 + 1) + 10)
* random得到double型
*/
public class ArrayTest1 {
public static void main(String[] args){
int[] arr = new int[10];
for(int i = 0;i < arr.length;i++){
arr[i] = (int)(Math.random() * (99 - 10 + 1) + 10);
}
//遍历
for(int i = 0;i <arr.length;i++){
System.out.println(arr[i]);
}
System.out.println();
//求数组最大值
//int max = 0;
int max = arr[0];
for(int i = 0;i <arr.length;i++){
if(max < arr[i]){
max = arr[i];
}
}
System.out.println(max);
//求数组最小值
int min =arr[0];
for(int i = 0;i <arr.length;i++){
if(min > arr[i]){
min = arr[i];
}
}
System.out.println(min);
//求数组总和
int sum = 0;
for(int i = 0;i <arr.length;i++){
sum += arr[i];
}
System.out.println(sum);
//求数组平均数
int avg = sum / arr.length;
System.out.println(avg);
}
}
练习四
/*
* 使用简单数组
(1)创建一个名为ArrayExer5的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
(3)显示array1的内容。
(4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。
*
* 思考:array1和array2是什么关系?array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体。
* 拓展:修改题目,实现array2对array1数组的复制
*/
public class ArrayExer5 {
public static void main(String[] args){
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//显示array1内容
for (int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
//赋值array2变量等于array1
array2 = array1;//仅仅将array1的地址给了array2,但实际上只有一个数组,仅仅是地址值一样
//只有通过new才会产生新的数组,这里只有一个new
//此处不能称作数组的复制
//修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)
for(int i = 0; i < array2.length;i++){
if(i % 2 ==0){
array2[i] = i;
}
}
System.out.println();
//打印出array1
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
}
}
地址值的变化并非复制
==复制文件与复制快捷方式的区别==
//数组的复制
public class ArrayExer6 {
public static void main(String[] args){
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
//显示array1内容
for (int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
//赋值array2变量等于array1
array2 = new int[array1.length];
for(int i = 0;i < array2.length;i++){
array2[i] = array1[i];
}
数组的复制操作
练习五
/*
* 算法的考查:数组的复制、反转、查找(线性查找、二分法查找)
*
*
*/
public class ArrayTest2 {
public static void main(String[] args) {
String[] arr = new String[]{"JJ","DD","MM","BB","GG","AA"};
//数组的复制(区别于数组变量的赋值:arr1 = arr)
String[] arr1 = new String[arr.length];
for(int i = 0;i < arr1.length;i++){
arr1[i] = arr[i];
}
//数组的反转
//法一:注意下标和长度的区别
for(int i = 0;i < 0.5 * arr.length;i++){
String transition = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = transition;
}
//法二:
// for(int i = 0,j = arr.length - 1;i < j;i++,j--){
// String transition = arr[i];
// arr[i] = arr[j];
// arr[j] = transition;
// }
for (int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//查找
//线性查找
String dest = "BB";
//equals.:比较字符串内的内容是否相同
boolean isFlag = true;//标识判断
for(int i = 0;i < arr.length;i++){
if(dest.equals(arr[i])){
System.out.println("找到了指定的元素:位置为:" + i);
isFlag = false;
break;
}
}
if(isFlag){
System.out.println("很遗憾,未找到");
}
//二分法查找:相对于线性查找,更快速
//前提:所要查找的数组必须有序
int[] arr2 = new int[]{-98,-40,-34,-5,3,4,25,34,90};
int dest1 = -34;
int head = 0;//初始的索引;
int end = arr2.length - 1;//初始的末索引
boolean isFlag1 = true;
while (head <= end){
int middle = (head + end)/2;
if(dest1 == arr2[middle]){
System.out.println("找到了指定的元素,索引位置为" + middle);
isFlag1 = false;
break;
}else if(arr2[middle] > dest1){
end = middle - 1;
}else{
head = middle + 1;
}
}
if(isFlag1){
System.out.println("很遗憾,未找到");
}
}
}
二分法
排序算法
排序:假设含有n个记录的序列为{R1,R2,...Rn},其相应的关键字序列为{K1,K2,...Kn}。将这些记录重新排序为{Ri1,Ri2,...<Rin},使得相应的关键字值满足条件Ki1<=Ki2<=Ki3<=...<=Kin,这样的一种操作称为排序。
通常来说,排序的目的是为了快速查找
-
衡量排序算法的优劣:
- 时间复杂度:分析关键字的比较次数和记录的移动次数
- 空间复杂度:分析排序算法中需要多少辅助内存
- 稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种算法是稳定的。
-
排序算法分类:内部排序和外部排序
- 内部排序:整个排序过程中不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
- 外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
-
十大内部排序算法
- 选择排序
- 直接选择排序
- *堆排序 *
- 交换排序
- ==冒泡排序==
- ==快速排序==
- 插入排序
- 直接插入排序
- 折半插入排序
- Shell排序(希尔排序)
- 归并排序
- 桶式排序
- 基数排序
- 选择排序
-
算法的5大特征
- 输入(input):有0个或多个输入数据,这些输入数据必须有清楚的描述和定义
- 输出(Output):至少有1个或多个输出结果,不可以没有输出结果
- 有穷性(有限性,Finiteness):算法在有限的步骤之后会自动结束而不会无线循环,并且每一个步骤可以在可接受的时间内完成
- 确定性(明确性,Definiteness):算法的每一步都有确定的含义,不会出现二义性
- 可行性(有效性,Effectiveness):算法的每一步都是清楚且可行的,能让用户用纸笔计算求出答案
==人们也关注并不要求终止的计算描述,这种描述有时被称为过程(procedure)==
冒泡排序
- 比较相邻的元素。如果第一个二大(升序),就交换他们两
- 对每一相邻元素作同样的工作,从开始第到结尾最后。这步做完后,最后的元素会是大数。
- 针对所有的元素重复以上步骤,除了最后一个。
- 持续每次对越来少的元素重复上面步骤,直到没有任何一数字需要比较为止。
public class BubbleSortTest {
public static void main(String[] args) {
int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99};
//冒泡排序
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]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
for(int i = 0;i < arr.length - 1;i++){
System.out.print(arr[i] + "\t");
}
}
}
快速排序
- 快速排序通常明显比O(nlogn)的其他算法更快,因此常被采用,快速排序算法采用了分治法的思想
排序思想
- 从数列中挑出一个元素,称为"基准"(pivot)
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
- 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去
快速排序过程
/**
* 快速排序
* 通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,
* 则分别对这两部分继续进行排序,直到整个序列有序。
*/
public class QuickSort {
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);//递归调用
subSort(data, high + 1, end);
}
}
public static void quickSort(int[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
}
排序算法的的性能对比
- 从平均时间而言 :快速排序最佳。 但在最坏情况下时间性能不如堆排序和归并排序。
- 从算法简单性看 :由于直接选择排序、插入和冒泡的算法比较简单,将其认为是简单算法。 对于Shell排序 、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。
- 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排序、快速排序、shell排序和堆排序是不稳定排序。
- 从待排序的记录数n的大小看 ,n较小时,宜采用简单排序;而 n较大时宜采用改进排序
排序算法的性能
Arrays工具类的使用
- java.util.Arrays类即为操作数组的工具类,包含用来操作数组(比如排序和搜索)的各种方法。
Arrays工具类常见方法
import java.util.Arrays;
/*
* java.util.Arrays:操作数组的工具类,里面定义了很多操作数组的方法
*
*
*/
public class ArraysTest {
public static void main(String[] args) {
//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。数组要有顺序
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals);
//2.String toString(int[] a):输出数组信息。
System.out.println(Arrays.toString(arr1));
//3.void fill(int[] a,int val):将指定值填充到数组之中。
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));
//4.void sort(int[] a):对数组进行排序。
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
//5.int binarySearch(int[] a,int key)
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int index = Arrays.binarySearch(arr3, 210);
if(index >= 0){
System.out.println(index);
}else{
System.out.println("未找到");
}
}
}
数组使用中的常见异常
/*
* 数组中的常见异常:
* 1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
*
* 2. 空指针异常:NullPointerException
*
*/
public class ArrayException {
public static void main(String[] args) {
//1. 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion,不在指定的范围内的情况
int[] arr = new int[]{1,2,3,4,5};
// for(int i = 0;i <= arr.length;i++){
// System.out.println(arr[i]);
// }此处出现了角标5
// System.out.println(arr[-2]);
// System.out.println("hello");
//2.2. 空指针异常:NullPointerException
//情况一:
// int[] arr1 = new int[]{1,2,3};
// arr1 = null;
// System.out.println(arr1[0]);
//情况二:
// int[][] arr2 = new int[4][];
// System.out.println(arr2[0][0]);
//情况三:
String[] arr3 = new String[]{"AA","BB","CC"};
arr3[0] = null;
System.out.println(arr3[0].toString());
}
}
数组重点
- 一维数组使用!!!
- 二维数组使用!!!