第一节:算法 查找
一.顺序查找
import java.util.Arrays;
/**
*顺序查找
*/
public class TestSearch {
public static void main(String[] args) {
int[] arr=creatArray(10,0,21);
System.out.println(Arrays.toString(arr));
int key=10;
System.out.println(search(arr,key));
}
//对指定的数组进行搜索,查找是否存在指定的值,如果存在返回该值的索引,不存在返回-1
//时间复杂度T(n)=O(n),空间复杂度S(n)=O(1);
public static int search(int[] arr,int key) {
if (arr == null)
return -1;
int LEN = arr.length;
if (LEN == 0)
return -1;
//常规情况,遍历数组
for (int i = 0; i < LEN; i++) {
if (arr[i] == key) {
return i;
}
}
//如果上面没找到,则返回-1
return -1;
}
//定义一个创建数组的方法
public static int[] creatArray(int len,int min,int max){
int[] arr=new int[len];
for (int i = 0; i < len; i++) {
arr[i]=randomNumber(min,max);
}
return arr;
}
//定义一个产生随机数的方法
public static int randomNumber(int min,int max){
return (int)(Math.random()*(max-min)+min);
}
}
2.二分搜索
概念:对待搜索区进行不断的拆分来缩小搜索的范围,实现最终查找的目的。
对搜索的数据的要求:1:需要是一个线性的数列。2:数列需要有序,升序的。
待搜索的数据为21:
/**
* 测试二分查找
*/
public class TestBinarySearch {
public static void main(String[] args) {
int[] array=TestSearch.creatArray(20,0,30);
//对数组进行排序
Arrays.sort(array);
System.out.println(Arrays.toString(array));
int key=9;
//测试自定义递归二分查找方法
//int index=binarySearchRecursion2(array,key);
//测试自定义二分查找方法(用循环写的二分查找法)
int index=binarySearch(array,key);
System.out.println("index="+index);
}
//自定义二分查找方法,使用循环实现,对指定的数组进行二分搜索,查找指定的值
//时间复杂度:T(n)=O(log2n) 空间复杂度:S(n)=O(1)
public static int binarySearch(int[] arr,int key){
//先判断特殊情况
if(arr==null)
return -1;
if(arr.length==0)
return -1;
//常规情况
int low=0;
int high=arr.length-1;
int mid=low+high >> 1;//右移以为相当于除以2,位运算符的优先级高于加号;等价于mid=(low+high)/2;
while(high>=low){
//取索引为中点的值
int value=arr[mid];
if(key==value){
return mid;
}else if(key<value){ //待搜索的值在mid的左边
high=mid-1;
}else{ //待搜索的值在mid的右边
low=mid+1;
}
//重新定义二分点
mid=low+high >> 1;
}
//如果在上面的循环中没有找到则返回-1;
return -1;
}
//使用递归实现二分搜索
//对知指定的数组的指定的区间,进行一次二分搜索的行为
public static int binarySearchRecursion(int[] arr,int key,int low,int high){
//递归终止条件
if(high<low){
return -1;
}
//使用递归实现
int mid=high +low >> 1;//求中间值
int midValue=arr[mid];
if(key==midValue){
return mid;
}else if(key>midValue){//在mid的右边
low=mid+1;
return binarySearchRecursion(arr,key,low,high);
}else{ //在mid的左边
high=mid -1;
return binarySearchRecursion(arr,key,low,high);
}
}
//对指定的数组进行递归二分
public static int binarySearchRecursion2(int[] arr,int key){
if(arr==null){
return -1;
}
final int LEN=arr.length;
if(LEN==0){
return -1;
}
int low=0;
int high=LEN-1;
return binarySearchRecursion(arr,key,low,high);
}
}
第二节 算法 排序
一、相关概念
稳定排序:如果一个数列中有相同的内容,排序之后,相同的内容的相对的前后位置没有发生变化,那么就是稳定的排序。
不稳定排序:如果一个数列中有相同的内容,排序之后,相同的内容的相对的前后位置可能发生变化,那么就是不稳定的排序。
内部排序:所有待排序的数据都在当前的内存中称为内部排序。
外部排序:待排序的数据量很大,全部放到内存中,内存不够使用,需要依赖于外部存储设备。把一部分数据放到外设中。
二、分类
1、.直接选择排序
2.直接插入排序
import java.util.Arrays;
/**
* 选择排序和插入排序代码
*/
public class TestSort {
public static void main(String[] args) {
int[] arr=TestSearch.creatArray(10,0,21);
System.out.println(Arrays.toString(arr));
//测试选择排序算法
selectSort(arr);
//测试插入排序算法
//insertSort(arr);
System.out.println(Arrays.toString(arr));
}
//使用选择算法对指定的数组进行排序
public static void selectSort(int[] arr){
//处理特殊情况
if(arr==null)
return;
final int LEN=arr.length;
if(arr.length==0)
return;
//常规情况
//i既用来控制为循环的趟数,还是代表了待排序区的第一个元素的索引
for (int i = 0; i < LEN-1; i++) {
//minIndex 存的是待排序区的最小值的索引
int minIndex=i;
//内层循环用于第i趟的最小元素索引的查找
for (int j = i+1; j < LEN; j++) {
if(arr[j]<arr[minIndex]){
//minIndex就保存了最小值的索引
minIndex=j;
}
}
//互换位置
if(minIndex!=i){ //待排序区肚饿第一个元素不是最小值
int temp=arr[i];
arr[i]=arr[minIndex];
arr[minIndex]=temp;
}
}
}
//使用直接插入排序对指定的数组进行排序
public static void insertSort(int[] arr){
//特殊情况处理
if(arr==null)
return;
final int LEN=arr.length;
if(LEN==0)
return;
//外层循环控制趟数,并且外层循环的i代表了无序区的第一个元素的索引
for (int i = 1; i < LEN; i++) {
//备份无序区的第一个元素
int temp=arr[i];
//扩大j的作用域
int j=0; //初始化j的值
//从后往前比较,i-1代表了有序区的最后一个元素的索引
for (j = i-1; j >=0 && arr[j]>=temp ; j--) {
arr[j+1]=arr[j];
}
//将备份的值填到最后被以后的那个下标的位置
arr[j+1]=temp;
}
}
}
三、数组的扩容
import java.util.Arrays;
/**
*数组扩容
*/
public class TestArrayExpansion {
public static void main(String[] args) {
int[] arr = {23,34,45,3,2};
//新创建一个数组,长度是现有数组的1.5倍
int[] newArr=new int[arr.length*3 >> 1];
//将原来的数组复制过来
System.arraycopy(arr,0,newArr,0,arr.length);
//在将需要添加的数据添加到数组元素尾部
newArr[arr.length]=77;
//将原数组的引用指向新数组
arr=newArr;
//将上面内容用一句代码实现
arr=Arrays.copyOf(arr,arr.length*3>>1);
System.out.println(Arrays.toString(arr));
}
}
第二节、数据结构
概念
数据结构:一组具有某些关系的数据的集合。
学习数据结构:主要就是学习数据之间的关系,关系决定了数据被访问的特点。
逻辑结构:数据之间的逻辑关系。(例如:一个学生拥有一本书,学生对象里面真的有一本书吗,只是存了这个书对象的引用)
存储结构:数据之间的存储的关系。
一 、数组
一种很简单的数据结构。
元素之间的关系:元素之间是连续的,通过一个个连续的序号可以进行访问。(是线性结构)
元素的特点:根据索引访问元素的效率很高,遍历元素的效率很高、删除元素,插入元素、效率比较低。
二、栈:一种线性关系的数据结构。
自己写了一个栈,代码如下:
/**
* 手写栈
*/
import com.sun.org.apache.xpath.internal.SourceTree;
import java.util.Arrays;
public class Test {
public static void main (String[] args) throws Exception{
MyStack myStack=new MyStack();
myStack.push("a");
myStack.push("_");
myStack.push(true);
myStack.push(1.1);
myStack.push(false);
System.out.println(myStack.toString());
System.out.println(myStack.search(true));
System.out.println(myStack.pop());
System.out.println(myStack.pop());
System.out.println(myStack);
}
}
//设计类来描述栈
class MyStack{
//定义栈的初始容量
public static final int DEFAULT_CAPACITY = 6;
//底层使用数组实现
private Object[] elementDate;
//栈顶指针
private int index;
public MyStack(){
elementDate=new Object[DEFAULT_CAPACITY];
//index=0; 这一句不写的话,默认值也是零
}
public MyStack(int initCapacity){
elementDate=new Object[initCapacity];
//index=0;
}
//压栈操作
public void push(Object o){
//栈满了,扩容,扩容的规则:1.5倍
/*
if(isFull()){
//扩容
elementDate=Arrays.copyOf(elementDate,elementDate.length*3>>1);
elementDate[index]=o;//容量不够先扩容,再在尾部增加数据
} else{
elementDate[index]=o;//容量够的话直接在尾部增加数据
index++;
}
*/
//上面的代码有相同的部分,可以进行优化,并且写的更高级 如下所示:
if(isFull()){
//扩容
Arrays.copyOf(elementDate,elementDate.length*3>>1);
}
elementDate[index++]=o;//将上面的两句代码合为一句。index++,先赋值再自增;
}
//弹栈或者是出栈操作
public Object pop() throws Exception{
//栈空了
if(isEmpty()){
throw new Exception("感觉身体被掏空");
}
//指针下移,然后将指针指向的位置获得
//index--;
Object o=elementDate[--index];
//以免内存泄漏
elementDate[index]=null;
return o;
}
//获得栈顶元素,但不移除
public Object peek() throws Exception{
if(isEmpty()){
throw new Exception();
}else{
return elementDate[index-1];
}
}
//o是否在栈中,存在返回索引,否则返回-1
public int search(Object o){
if(isEmpty())
return -1;
for (int i = 0; i < index; i++) {
if(elementDate[i].equals(o))
return i;
}
return -1;
}
//得到栈的容量
public int capacity(){
return elementDate.length;
}
//得到栈中元素的个数
public int size(){
return index;
}
//得到栈的字符串表示形式
public String toString(){
return Arrays.toString(Arrays.copyOf(elementDate,index));//这样就可以将栈中存的null删除,并且得到原数组
}
//判断栈是否满了,满了返回true,否则返回false
public boolean isFull(){
return index == elementDate.length;
}
//判断栈是否为空
public boolean isEmpty(){
return index==0;
}
}
三、链表
也是一种线性的数据结构。每个元素也有一个序号,元素添加的顺序号。0序的。
四、树 tree
非线性的数据结构。
今日练习:
1:自己实现二分查找法(循环+递归)要求两种实现。
2:实现冒泡排序。
3:实现选择排序。
4:实现插入排序。
5:实现对指定的目录,统计该目录中文件的类型信息。将所有的文件的后缀返回。不能有重复的后缀。要求返回的格式为[.txt;.jpg;.dat]。后缀名之间使用 分号 分割。
import java.io.File;
//实现对指定的目录,统计该目录中文件的类型信息。
// 将所有的文件的后缀返回。不能有重复的后缀。要求返回的格
// 式为[.txt;.jpg;.dat]。后缀名之间使用 分号 分割。
public class Test2 {
public static void main(String[] args) {
System.out.println(countFileTypes(new File("c:/7")));
}
/**
* @param file 被统计的目录
* @return 返回 file 目录下的所有的文件的类型的信息。
*/
public static String countFileTypes(File file){
StringBuilder sb = new StringBuilder();
sb.append("[");
if(file.isDirectory()){
//得到目录下的所有的子文件
File[] files = file.listFiles();
for (File f : files) {//ni.a.txt
//如何去获得每一个文件的后缀信息
String name = f.getName();
//找最后一个 点的 索引值
int index = name.lastIndexOf(".");
//获得了包含 . 后缀的信息
String suffix = name.substring(index);
//sb 中 没有suffix 的话 添加到尾部。
if(sb.indexOf(suffix) < 0){
sb.append(suffix).append(";");
}
}
}
sb.append("]");
return sb.toString();
}
}