排序
基数排序
介绍:
基本思想:
代码:
package 排序算法.基数排序;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class _基数排序_ {
public static void main(String[] args) {
// int[]arr = {53, 3, 542, 748, 14, 214};
// radixSort(arr);
// System.out.println(Arrays.toString(arr));
int[] arr = new int[80000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*80000);
}
Date date1 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-ss HH:mm:ss");
String str1 = sdf.format(date1);
System.out.println(str1);
radixSort(arr);
Date date2 = new Date();
String str2 = sdf.format(date2);
System.out.println(str2);
}
public static void radixSort(int []arr){
// 求出最大的数据 求出几位数
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(max<arr[i]){
max = arr[i];
}
}
int maxLength = (max+"").length();
// 定义数组
int [][]bucketArray = new int[10][arr.length];// 桶里的数剧
int []bucketOfcounts = new int[10];// 每个桶实际有多少个数据
// 桶排序实现maxLength次数
for (int i = 0,n = 1; i < maxLength; i++,n*=10) {
// 进行每一次的桶排序,不同位数的排序
for (int j = 0; j < arr.length; j++) {
int val = arr[j]/n%10;// 取出相应位置上的数据把他放在对应的桶里面
bucketArray[val][bucketOfcounts[val]] = arr[j];
bucketOfcounts[val] ++;// 放入一个数据之后就相应的统计桶里面实际的元素的个数
}
// 在桶里面放完数据之后吧这些数据放回到原来的数组中
int cnt = 0;
for(int k=0;k< bucketOfcounts.length;k++){
if(bucketOfcounts[k]!=0){
for (int l = 0; l < bucketOfcounts[k]; l++) {
arr[cnt++] = bucketArray[k][l];
}
}
bucketOfcounts[k] = 0; // 放完之后对下一轮排序做准备
}
}
}
public static void radixSortOfTest(int[]arr){
// 定义二维数组表示十个桶
// 包含了十个一维数组,每个一维数组就是一个桶,为了防止放数的时候桶溢出,大小是arr.length
int [][]bucket = new int[10][arr.length];
// 为了记录每个桶中实际放了多少的数据,我们定义一维数组来记录每次放入数据的个数
int []bucketElementCounts = new int[10];
for (int i = 0; i < arr.length; i++) {
for(int j = 0;j<arr.length;j++){
// 取出每个元素的个位
int digitOfElement = arr[j]/10%10;
// 放入到对于的桶中,记录放入的个数
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
int index = 0;
// 遍历每一个桶,将桶中数据放入原数组
for(int k = 0;k<bucketElementCounts.length;k++){
// 如果桶中有数据,我们放入到原数组
if(bucketElementCounts[k]!=0){
// 循环改桶即第k个桶 ,放入
for(int l = 0;l<bucketElementCounts[k];l++){
arr[index++] = bucket[k][l];
}
}
}
System.out.println("第一轮对个位的排序结束");
System.out.println(Arrays.toString(arr));
}
}
}
总结:
排序算法时间复杂度比较
相关术语解释:
- 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
- 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
- 内排序:所有排序操作都在内存中完成;
- 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
- 时间复杂度: 一个算法执行所耗费的时间。
- 空间复杂度:运行完一个程序所需内存的大小。
- n: 数据规模
- k: “桶”的个数
- In-place: 不占用额外内存
- Out-place: 占用额外内存
查找算法
线性查找
二分查找
思路:
代码:
package 查找算法;
import java.util.ArrayList;
import java.util.List;
// 使用二分查找的前提是数组是有序的
public class _二分查找_ {
public static void main(String[] args) {
int arr[] = {1,8, 10, 89, 1000, 1000,1234};
ArrayList<Integer> arrayList= binarySearch(arr,0,arr.length-1,1000);
System.out.println(arrayList);
}
/**
*
* @param arr 数组
* @param left 左边的索引
* @param right 右边的索引
* @param findVal 要找的值
* @return 如果找到返回下标 没有找到返回-1
*/
public static ArrayList<Integer> binarySearch(int[]arr, int left, int right, int findVal){
if(left>right){
return new ArrayList<>();
}
int mid = (left+right)/2;
int midVal = arr[mid];
if(findVal > midVal){
return binarySearch(arr,mid+1,right,findVal);
}else if(findVal < midVal){
return binarySearch(arr,left,mid-1,findVal);
}else {
// return mid;
// 如果要找到所有的值,我们先不返回,将这些下标放到arrayList中 ,然后向做左、右扫描,返回ArrayList
ArrayList<Integer> resIndexlist = new ArrayList<>();
// 往左边找
int temp = mid - 1;
while(true){
if(temp<0||arr[temp] != findVal){
break;
}
resIndexlist.add(temp);
temp -= 1;
}
// 往右边找
temp = mid+1;
while(true){
if(temp>arr.length-1||arr[temp] != findVal){
break;
}
resIndexlist.add(temp);
temp += 1;
}
resIndexlist.add(mid);
return resIndexlist;
}
}
}
插值查找
思路:
key
代码:
package 查找算法;
import java.util.Arrays;
//要求数组是有序的 比较紧的连续值
public class _插值查找_ {
public static void main(String[] args) {
int []arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i] = i+1;
}
int index = InsertSearch(arr,0,arr.length-1,10);
System.out.println(index);
System.out.println(Arrays.toString(arr));
}
/**
* 要求数组是有序的 比较紧的连续值
* @param arr 数组
* @param left 左边下标
* @param right 右边下标
* @param findVal 要找的值
* @return
*/
public static int InsertSearch(int[]arr,int left,int right,int findVal){
if(left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
return -1;
}
// 求出mid
int mid = left + (right - left)*(findVal - arr[left]) / (arr[right] - arr[left]);
int midVal = arr[mid];
// 往右边找
if(findVal>midVal){
return InsertSearch(arr,mid+1,right,findVal);
}else if(findVal<midVal){
return InsertSearch(arr,left, mid-1,findVal);
}else{
return mid;
}
}
}
斐波那契查找
思路:
原理:
其中的n值是任意的,意味着任何的数组都能找到对应的斐波那契数组
斐波那契查找的整个过程可以分为:
1.构建斐波那契数列;
2.计算数组长度对应的斐波那契数列元素个数;
3.对数组进行填充;
4.循环进行区间分割,查找中间值;
5.判断中间值和目标值的关系,确定更新策略;
代码实现:
package 查找算法;
import java.util.Arrays;
// 非递归实现法
public class _斐波那契查找_ {
public static int maxSize = 20;
public static void main(String[] args) {
int []arr = {1,8, 10, 89, 1000, 1234};
System.out.println(fibSearch(arr,1));
}
// 需要菲波那切数列 mid = low + f[k-1]-1;
public static int[] fibo(){
int f[] = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i-1] + f[i-2];
}
return f;
}
/**
*
* @param arr
* @param findVal 要找的关键码值
* @return 返回下标,没有-1
*/
public static int fibSearch(int []arr,int findVal){
int low = 0;
int high = arr.length-1;
int k = 0;//表示斐波那契分割数值的下标
int mid = 0;// 存放mid值
int f[] = fibo();// 获取斐波那契数列
// 获取到斐波那契数列分割的下标
// 就是找到最近的fibonacci序列代表的元素的个数,如果比arr.length大我们就填充
while(high>f[k]-1){
k++;
}
// f[k] 对应的值不一定和数组大小相同
// 因为f[k]值可能大于a的长度,我们需要使用Arrays类,构造一个新的数组,并指向a[]
// 不足的部分是用0填充
int []temp = Arrays.copyOf(arr,f[k]); // 原来的数组 数组长度
System.out.println(Arrays.toString(temp));
// 实际上使用a数组最后的数填充temp
for (int i = arr.length; i < temp.length; i++) {
temp[i] = arr[high];
}
System.out.println(Arrays.toString(temp));
// 使用while来循环处理,找到我们的key
while(low<=high){
mid = low + f[k-1] -1;
if(findVal<temp[mid]){// 我们应该向数组前面找
high = mid-1;
// 前面的数组长度是f[k-1] 为下次的划分做准备
k--;
}else if(findVal>temp[mid]){
low = mid+1;
// 后面的数组长度是f[n-2] 为下次的划分做准备
k-=2;
}else{// 找到,确定返回那个下标
// 可能会出现这个下标大于原来的长度
if(mid>arr.length){
return arr.length-1;
}else{
return mid;
}
}
}
return -1;
}
}
哈希表
使用场景
在缓冲区临时保存需要查找的区域
例如:
基本介绍:
案例简单实现:
package 哈希表;
import java.util.Arrays;
import java.util.Scanner;
public class _哈希表_ {
public static void main(String[] args) {
HashTable hashTable = new HashTable(7);
String key = "";
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("add:添加雇员");
System.out.println("show:显示雇员");
System.out.println("find:查找雇员");
System.out.println("exit:退出系统");
key = scanner.next();
switch (key){
case "add":
System.out.println("输入id");
int id = scanner.nextInt();
System.out.println("输入年龄");
int age = scanner.nextInt();
System.out.println("输入名字");
String name = scanner.next();
Emp emp = new Emp(id,age,name);
hashTable.addEmp(emp);
break;
case "find":
System.out.println("输入id");
int id1 = scanner.nextInt();
hashTable.findEmpById(id1);
break;
case "exit":
scanner.close();
System.exit(-1);
case "show":
hashTable.listAllEmp();
break;
default:
break;
}
}
}
}
// 表示雇员
class Emp{
public int id;
public int age;
public String name;
public Emp next;
public Emp(int id ,int age,String name){
super();
this.id = id;
this.age = age;
this.name = name;
}
}
// 表示链表
class EmpLinkedList{
public Emp head;
public void add(Emp emp){
if(head == null){
head = emp;
return;
}
// 不考虑添加问题 ,直接加到最后
Emp curEmp = head;
while(true){
if(curEmp.next == null){
break;
}
curEmp = curEmp.next;
}
curEmp.next = emp;
return;
}
public void list(int i){
if(head==null){
System.out.printf("这个链表%d是空的~~\n",i);
return;
}
Emp curEmp = head;
while(true){
System.out.printf("===>第%d条链表,这个员工是%s,%d \n",i,curEmp.name,curEmp.age);
if(curEmp.next==null){
break;
}
curEmp = curEmp.next;
}
}
// 根据id查找雇员
public Emp findEmpById(int id){
if(head==null){
System.out.println("链表空");
}
Emp curEmp = head;
while(true){
if(curEmp.id == id){
return curEmp;
}
curEmp = curEmp.next;
if(curEmp==null){
break;
}
}
return null;
}
}
//创建哈希表
class HashTable{
private EmpLinkedList[] empLinkedListsArray;
private int size;
public HashTable(int size){
this.size = size;
empLinkedListsArray = new EmpLinkedList[size];
// 还需要初始化每一个链表
for (int i = 0; i < size; i++) {
empLinkedListsArray[i] = new EmpLinkedList();
}
}
// 添加雇员
public void addEmp(Emp emp){
int EmpLinkedListsIndex = hashFun(emp.id);
empLinkedListsArray[EmpLinkedListsIndex].add(emp);
}
// 遍历
public void listAllEmp(){
for (int i = 0; i < size; i++) {
empLinkedListsArray[i].list(i);
}
}
// 查找
public void findEmpById(int id){
int index =hashFun(id);
Emp emp = empLinkedListsArray[index].findEmpById(id);
if(emp!=null){
System.out.printf("找到了第%d条链表,名字是%s\n",id,emp.name);
return;
}
System.out.println("没找到");
}
//散列函数
public int hashFun(int id){
return id % size;
}
}