目录
练习题:请在控制台上输入一个数组,长度为10,类型是整数,求该数组的最大值,最小值,平均值
1. 数组
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式,对这些数据进行统一管理。
数组是一种连续内存、大小固定的线性表,数据结构,用来解决大量数组的存储问题。
学习数组是为了解决数据存储的问题。
1.1 容器(collection)
容器一般是一种数据结构。是用来解决多个数据保存和计算的容器
数组就是一种典型的容器,
线性表:数组、栈、队列、链表,哈希表,树,图:多维结构,矩阵
1.2 数组的特点
1. 大小固定:不能保证在扩容时后面还有空间,所以数组的长度一旦确定,就不能修改
2. 连续内存地址:创建数组对象会在内存中开辟一整块连续的空间
3. 数组存储的数据类型固定,数组的元素,既可以是基本数据类型,也可以是引用数据类型。
4. 数组是保存在堆内存中的!!!因此数组是对象。
1.3 数组的分类
① 按照维度:一维数组、二维数组、。。。。
② 按照数组元素的类型,基本数据类型元素的数组、引用数据类型元素的数组
1.4 Java中如何定义数组
1. 第一种定义方式
数据类型[] 变量名称 = new 数据类型[length];
2. 第二种定义方式
数据类型[] 变量名称 = new 数据类型[] {值1, 值2, 值3,……};
3. 第三种定义方式
数据类型[] 变量名称 = {值1, 值2, 值3,……};
// 定义数组:推荐第一种或第三种
// 第一种定义方式
// 数据类型[] 变量名称 = new 数据类型[size];
int[] arr = new int[4];
// 第二种定义方式
// 数据类型[] 变量名称 = new 数据类型[]{值1, 值2……};
int[] arr1 = new int[] {1, 2, 3, 4};
// 第三种定义方式
// 数据类型[] 变量名称 = {值1, 值2……};
int[] arr2 = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(arr)); // [0, 0, 0, 0]
System.out.println(Arrays.toString(arr1)); // [1, 2, 3, 4]
System.out.println(Arrays.toString(arr2)); // [1, 2, 3, 4, 5]
1.5 数组的长度(元素的个数)
数组对象.length() : // 属性
// 查看数组的长度
System.out.println(arr2.length); // 5
1.6 访问数组元素(element):
通过下标(索引或角标)来访问,注意:在编程中,%99的情况下,下标都是从0开始的
数组对象[下标] // 通过下标来访问元素的个数
// 访问数组元素(element):下标从零开始到arr.length - 1
System.out.println(arr[0]); // 0
System.out.println(arr1[3]); // 4
System.out.println(arr2[4]); // 5
1.6 修改元素的数值
数组对象[下标] = 新值;
// 修改元素的数值:
arr[0] = 10;
System.out.println(arr[0]); // 10
1.7 如何遍历数组元素
方法:制造一个下标
1. for循环
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
2. while循环
int i = 0;
while(i < arr.length) {
System.out.println(arr[i]);
i++;
}
3. foreach:循环加强,是jdk5的新特性,是一种迭代容器操作
for (数据类型 临时变量: 可迭代对象) {
System.out.println(临时变量);
}for (int item: arr) {
System.out.println(item);
}
// 遍历数组元素
// for循环构建下标
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
// 1 2 3 4
System.out.println();
// while循环构建下标
int i = 0;
while (i < arr1.length) {
System.out.print(arr1[i] + "\t");
i++;
}
// 10 0 0 0
System.out.println();
// for each 循环加强
for (int item : arr2) {
System.out.print(item + "\t");
}
// 1 2 3 4 5
System.out.println(arr);//直接输出一个数组结果为地址值:[I@15db9742
/*
分析:"["表示数组,''I''表示数组中变量类型,"15db9742"表示内存地址,"@"用于分割类型与地址。
补充:“[[”表示二维数组
*/
1.8 数组元素的默认初始化值
数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。
> 数组元素是整形:0
> 数组元素是浮点型:0.0
> 数组元素是char型:0或'\u0000'(表现为空), 而非'0'
> 数组元素是boolean型:false
> 数组元素是引用数据类型:null 而非"null"
// 数组元素的默认初始化值
int[] arr = new int[4];
for(int i = 0;i < arr.length;i++) {
System.out.print(arr[i]); // 0000
}
System.out.println("\n----------");
short[] arr1 = new short[4];
for(int i = 0;i < arr1.length;i++) {
System.out.print(arr1[i]); // 0000
}
System.out.println("\n----------");
float[] arr2 = new float[5];
for(int i = 0;i < arr2.length;i++) {
System.out.print(arr2[i]); // 0.00.00.00.00.0
}
System.out.println("\n----------");
char[] arr3 = new char[4];
for(int i = 0;i < arr3.length;i++) {
System.out.println("****" + arr3[i] + "****"); // **** ****
}
if (arr3[0] == 0) {
System.out.println("你好!"); // 你好!
}
System.out.println("----------");
boolean[] arr4 = new boolean[4];
System.out.println(arr4[0]); // false
System.out.println("----------");
String[] arr5 = new String[5];
System.out.println(arr5[0]); // null
if (arr5[0] == "null") {
System.out.println("北京天气不错!");
}
1.9 数组的内存解析:内存中是如何分配数组结构的
内存结构的规范是在JVM中体现的。
栈(stack):线性结构,主要存放局部变量(例如main方法)
堆(heap):new出来的结构:对象、数组
方法区(method area):常量池、静态域 另外还包括一些类加载的一些信息
常量池:字符串String 静态域:static
int[] arr = new int[] {1, 2, 3};
String[] arr1 = new String[4];
arr1[1] = "刘德华";
arr1[2] = "张学友";
arr1 = new String[3];
System.out.println(arr1[1]); // null
int[] arr = new int[]{1,2,3};
首先,加载一个变量(arr)放到栈里面。
new了一个长度length为3连续的结构放到堆里面,这个结构会有一个首地址值(16进制数表示)
arr(定义在main方法下的都是局部变量):把首地址值赋给arr,通过这个地址值就可以(指向)找到堆空间的数组。开始的时候数组里面的默认值都是零,找到之后默认值替换掉
若没有栈当中的引用指向0x12ab,既不会被调用,故被垃圾回收机制清理(引用计数算法)
main方法执行完毕以后既出了作用域{},变量会出栈,出栈之后,因为没有栈当中的引用指向堆中空间的首地址值,既不会被调用,故被垃圾回收机GC制清理
练习题:请在控制台上输入一个数组,长度为10,类型是整数,求该数组的最大值,最小值,平均值
import java.util.Arrays;
import java.util.Scanner;
public class ArrayTest1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入10个数:");
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
int num = scanner.nextInt();
arr[i] = num;
}
System.out.println(Arrays.toString(arr));
findMaxMinAvge(arr);
scanner.close();
}
public static void findMaxMinAvge(int[] arr) {
// TODO Auto-generated method stub
int sum = 0;
int max = arr[0];
int min = arr[0];
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
if (max < arr[i]) {
max = arr[i];
}
if (min > arr[i]) {
min = arr[i];
}
}
int avge = sum / arr.length;
System.out.println("平均值:" + avge);
System.out.println("最大值:" + max);
System.out.println("最小值:" + min);
}
}
2. 算法
该算法值的是《数据结构与算法》中的算法。
算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
算法的5大特征:
说明:满足确定性的算法也称为:确定性算法。现在人们也关注更广泛的概念,例如 考虑各种非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终 止的计算描述,这种描述有时被称为过程(procedure)。
2.1 排序(sort)
将无序的数据安装特定规则排成有序数据(升序、降序)
内部排序和外部排序(数据过多无法在内存空间内完成,需要借助外部存储器)。有十几种,比较多,有的比较抽象。
常见的排序算法:十大内排(选择排序、堆排序、冒泡排序、快速排序、直接插入排序、折半插入排序、Shell排序、归并排序、桶式排序、基数排序)
这里我们主要讲一下:冒泡排序、选择排序、直接插入排序
扩展知识:值传递和引用传递的区别
值传递,仅仅就是发生了一次值的拷贝;
引用传递,是内存地址(对象)
public class VauleOrObjectTest {
public static void main(String[] args) {
int a = 10, b = 20;
change(a, b);
System.out.println("a = " + a + ", b = " + b); // a = 10, b = 20
int[] arr = {1, 2};
change(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); // 2 1
}
}
public static void change(int a, int b) {
// TODO Auto-generated method stub
int temp = a;
a = b;
b = temp;
}
public static void change(int[] arr) {
if (arr.length < 2) {
return;
}
arr[0] = arr[0] + arr[1];
arr[1] = arr[0] - arr[1];
arr[0] = arr[0] - arr[1];
}
}
2.1.1 冒泡排序
冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。大数上浮法或小数下沉法
冒泡排序的基本思想:
1.比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要
比较为止。
代码实现如下:
public class BubbleSortTest {
public static void main(String[] args) {
int[] arr = new int[] {47,15,22,-1,12,-22,32,54,12,8};
//冒泡排序:让相邻的两个元素进行比较,若逆序则交换,较大或者较小的元素逐渐从前部移向后部,得到一个最大值或最小值,
//然后重复如此,一直到length-1次(最开始的元素自动排序)。
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;i++) {
System.out.print(arr[i] + " ");
}
}
}
2.1.2 选择排序
假设第一个值为最小值,之后一直查找,直到找到真正的最小值,交换这两个数
原 1, 100, -10, 50, 3, -5, 96
1 -10, 100, 1, 50, 3, -5, 96
2 -10, -5, 1, 50, 3, 100, 96
3 -10, -5, 1, 50, 3, 100, 96
4 -10, -5, 1, 3, 50, 100, 96
5 -10, -5, 1, 3, 50, 100, 96
6 -10, -5, 1, 3, 50, 96, 100
基本思想:
1. 在一组元素R[i]到R[n]中选择具有最小关键码的元素
2. 若它不是这组元素中的第一个元素,则将它与这组元素中的第一个元素对调。
3. 除去具有最小关键字的元素,在剩下的元素中重复第1、2步,直到剩余元素只有一个为止。
代码实现:
public class TestSort {
public static void main(String[] args) {
int[] arr = {1, 100, -10, 50, 3, -5, 96};
System.out.println("排序前:") ;
for (int i : arr) {
System.out.print(i + ", ");
}
// 选择排序
// selectSort(arr);
selectSort02(arr);
System.out.println("\n排序后:") ;
for (int i : arr) {
System.out.print(i + ", ");
}
}
// 选择排序
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
// j对应的下标才是最小值
min = j;
}
}
// 假设失败,真正的最小值是min
if (min != i) {
swap(arr, min, i);
}
}
}
// 选择排序改进,让代码的稳定性增加,效率提高
public static void selectSort02(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[i]) {
swap(arr, j, i);
}
}
}
}
private static void swap(int[] arr, int i, int j) {
arr[j] = arr[j] ^ arr[i];
arr[i] = arr[j] ^ arr[i];
arr[j] = arr[j] ^ arr[i];
}
}
2.1.3 直接插入排序
以第一个为有序数组,之后的所有元素依次插入到这个有序数组中,插入时,必须保证数组一直有序!!!
基本思想:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
代码实现:
public class TestSort {
public static void main(String[] args) {
int[] arr = {1, 100, -10, 50, 3, -5, 96};
System.out.println("排序前:") ;
for (int i : arr) {
System.out.print(i + ", ");
}
// 插入排序
insertSort(arr);
System.out.println("\n排序后:") ;
for (int i : arr) {
System.out.print(i + ", ");
}
}
// 插入排序
public static void insertSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
// 取下一个元素,倒着插入,保证插入时数组一直有序
for (int j = i + 1; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
swap(arr, j, j - 1);
}
}
}
}
private static void swap(int[] arr, int i, int j) {
arr[j] = arr[j] ^ arr[i];
arr[i] = arr[j] ^ arr[i];
arr[j] = arr[j] ^ arr[i];
}
}
算法的特性:
算法的特征也是衡量排序算法的优劣:
1.时间复杂度:分析关键字的比较次数和记录的移动次数
冒泡:O(n^2)
选择:O(n^2)
2.空间复杂度:分析排序算法中需要多少辅助内存
3.稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。
如:[3,4,1,5,4,3′] ==>[1,2,3,3′,4,5]
冒泡:稳定的排序算法
选择:不稳定
2.2 查找
查找算法:都是针对有序数据而言的!!!这里讲一下二分查找
二分查找 (折半查找)
针对于有序的序列,可以直接查找中间值
基本思想:首先start = 0, end = arr.length - 1,找到数组中最中间的下标mid = (start + end) / 2,判断下标mid的元素和目标target是否相等,如果相等就找到了;如果下标mid的元素大于目标target,那么我们就在start到end = mid - 1这个范围内找中间下标,做判断,循环如此,如果下标mid的元素小于目标target,那么我们就start = mid + 1到end的范围内找中奖下标,,做判断,循环如此,直到找到下标mid的元素的值与traget相等结束,或当start > end时,还没有下标mid的元素与target匹配,则目标不在数组中,循环结束。
import java.util.Arrays;
public class QueryTest {
public static void main(String[] args) {
int[] arr = { 1, 100, -10, 50, 3, -5, 96 };
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
int target = 500;
int index1 = binarySearch1(arr, target, 0, arr.length - 1);
if (index1 == -1) {
System.out.println("很遗憾,没有找到!");
}else {
System.out.println("找到了指定的元素,位置为:" + index1);
}
int index2 = binarySearch2(arr, target, 0, arr.length - 1);
if (index2 == -1) {
System.out.println("很遗憾,没有找到!");
}
else {
System.out.println("找到了指定的元素,位置为:" + index2);
}
}
public static int binarySearch2(int[] arr, int target, int start, int end) {
if (start <= end) {
int middle = (start + end) >> 1;
if (arr[middle] == target) {
return middle;
}else if (arr[middle] < target) {
start = middle + 1;
return binarySearch2(arr, target, start, end);
}else if (arr[middle] > target) {
end = middle - 1;
return binarySearch2(arr, target, start, end);
}
}
return -1;
}
public static int binarySearch1(int[] arr, int target, int start, int end) {
// TODO Auto-generated method stub
// for (int i = start; i <= end; i = start) {
// int middle = (start + end) >> 1;
// if (arr[middle] == target) {
// return middle;
// } else if (arr[middle] > target) {
// end = middle - 1;
// } else {
// start = middle + 1;
// }
// }
// return -1;
while (start <= end) {
int middle = (start + end) >> 1;
if (arr[middle] == target) {
return middle;
}else if (arr[middle] > target) {
end = middle - 1;
}else if (arr[middle] < target) {
start = middle + 1;
}
}
return -1;
}
}