Java-Day07 数组、算法入门(排序、查询)详解

目录

1. 数组

1.1 容器(collection)

1.2 数组的特点

1.3 数组的分类

1.4 Java中如何定义数组

1.5 数组的长度(元素的个数)

1.6 访问数组元素(element):

1.6 修改元素的数值

1.7 如何遍历数组元素

1.8 数组元素的默认初始化值

1.9 数组的内存解析:内存中是如何分配数组结构的

练习题:请在控制台上输入一个数组,长度为10,类型是整数,求该数组的最大值,最小值,平均值

2. 算法

2.1 排序(sort)

扩展知识:值传递和引用传递的区别

2.1.1 冒泡排序

2.1.2 选择排序

2.1.3 直接插入排序

算法的特性:

2.2 查找

二分查找 (折半查找)


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;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Golang_HZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值