目录
概念
-
数据结构概念
数据的逻辑结构
线性结构: 线性表 栈 队列
非线性结构: 树形结构 图形结构
数据的储存结构 顺序储存、链式储存
数据的运算 检索、排序、插入、删除、修改等
-
四种基本机构
集合 线性结构 树形结构 图状结构或网状
稀疏数组
队列
有序列表 先入先出
class arrayqueue{
private int maxsize;
private int front;
private int rear;
private int[] arr;
//创建构造器
public arrayqueue(int arrmaxsize){
maxsize=arrmaxsize;
arr=new int[maxsize];
front=-1; //队列头部前一个位置
rear=-1; //队列尾部数据
}
public boolean isFull(){
return rear==maxsize-1;
}
public boolean isEmpty(){
return rear==front;
}
public void addQueue(int n){
if(isFull()){
System.out.println("队列满,不能加入数据。");
return;
}
rear++;
arr[rear]=n;
}
public int getQueue(){
if (isEmpty()){
throw new RuntimeException("队列空,不能取出数据。");
}
front++;
return arr[front];
}
public void showQueue(){
if (isEmpty()){
System.out.println("队列空,没有数据。");
return;
}
for (int i=0;i<arr.length;i++){
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
public int headQueue(){
if (isEmpty()){
throw new RuntimeException("队列空,不能显示数据。");
}
return arr[front+1];
}
}
环形队列
class CircleArray {
private int maxSize; // 表示数组的最大容量
//front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
//front 的初始值 = 0
private int front;
//rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
//rear 的初始值 = 0
private int rear; // 队列尾
private int[] arr; // 该数据用于存放数据, 模拟队列
public CircleArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
}
// 判断队列是否满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
// 判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
// 添加数据到队列
public void addQueue(int n) {
// 判断队列是否满
if (isFull()) {
System.out.println("队列满,不能加入数据~");
return;
}
//直接将数据加入
arr[rear] = n;
//将 rear 后移, 这里必须考虑取模
rear = (rear + 1) % maxSize;
}
// 获取队列的数据, 出队列
public int getQueue() {
// 判断队列是否空
if (isEmpty()) {
// 通过抛出异常
throw new RuntimeException("队列空,不能取数据");
}
// 这里需要分析出 front是指向队列的第一个元素
// 1. 先把 front 对应的值保留到一个临时变量
// 2. 将 front 后移, 考虑取模
// 3. 将临时保存的变量返回
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
// 显示队列的所有数据
public void showQueue() {
// 遍历
if (isEmpty()) {
System.out.println("队列空的,没有数据~~");
return;
}
// 思路:从front开始遍历,遍历多少个元素
// 动脑筋
for (int i = front; i < front + size() ; i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
// 求出当前队列有效数据的个数
public int size() {
// rear = 2
// front = 1
// maxSize = 3
return (rear + maxSize - front) % maxSize;
}
// 显示队列的头数据, 注意不是取出数据
public int headQueue() {
// 判断
if (isEmpty()) {
throw new RuntimeException("队列空的,没有数据~~");
}
return arr[front];
}
}
链表
节点存储 data next 不一定连续
单链表
//定义singlelinkedlist管理
class SingleLinkedList{
private HeroNode head=new HeroNode(0,"","");
public void add(HeroNode heroNode){
HeroNode temp=head;
while (true){
if (temp.next==null){
break;
}
temp=temp.next;
}
temp.next=heroNode;
}
public void addByOder(HeroNode heroNode){
//找加入节点的前一个位置
HeroNode temp=head;
boolean flag=false;//标识编号是否存在
while (true){
if (temp.next==null||temp.next.num>heroNode.num){
break;
}
else if(temp.next.num==heroNode.num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
System.out.printf("插入编号%d存在,不能加入\n",heroNode.num);
}
else {
heroNode.next=temp.next;
temp.next=heroNode;
}
}
public HeroNode getHead() {
return head;
}
//修改节点
public void update(HeroNode newheronode){
if (head.next==null){
System.out.println("链表为空");
return;
}
boolean flag=false;
HeroNode temp=head.next;
while (true){
if (temp==null){
break;
}
if (temp.num==newheronode.num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
temp.name=newheronode.name;
temp.nickname=newheronode.nickname;
}else {
System.out.printf("没有找到编号为%d的节点\n",newheronode.num);
}
}
//删除节点
public void del(int num){
if (head.next==null){
System.out.println("链表为空");
return;
}
HeroNode temp=head;
boolean flag=false;
while (true){
if (temp.next==null){
break;
}
if (temp.next.num==num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
temp.next=temp.next.next;
}else {
System.out.printf("没有找到要删除的编号为%d的节点\n",num);
}
}
public void list(){
if (head.next==null){
System.out.printf("链表为空");
return;
}
HeroNode temp=head.next;
while (true){
if (temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
//获取单链表的节点个数 不统计头结点
}
//定义heroNode
class HeroNode{
public int num;
public String name;
public String nickname;
public HeroNode next;
public HeroNode (int num,String name,String nickname){
this.num=num;
this.name=name;
this.nickname=nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"num=" + num +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
双链表
class DoubleLinkedList{
private HeroNode2 head=new HeroNode2(0,"","");
public HeroNode2 getHead() {
return head;
}
//遍历
public void list(){
if (head.next==null){
System.out.printf("链表为空");
return;
}
HeroNode2 temp=head.next;
while (true){
if (temp==null){
break;
}
System.out.println(temp);
temp=temp.next;
}
}
public void addByOder(HeroNode2 heroNode){
//找加入节点的前一个位置
HeroNode2 temp=head;
boolean flag=false;//标识编号是否存在
while (true){
if (temp.next==null||temp.next.num>heroNode.num){
break;
}
else if(temp.next.num==heroNode.num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
System.out.printf("插入编号%d存在,不能加入\n",heroNode.num);
}
else {
if (temp.next!=null){
temp.next.pre=heroNode;
}
heroNode.next=temp.next;
temp.next=heroNode;
heroNode.pre=temp;
}
}
//添加到最后
public void add(HeroNode2 heroNode){
HeroNode2 temp=head;
while (true){
if (temp.next==null){
break;
}
temp=temp.next;
}
//连接 双向
temp.next=heroNode;
heroNode.pre=temp;
}
//修改节点
public void update(HeroNode2 newheronode){
if (head.next==null){
System.out.println("链表为空");
return;
}
boolean flag=false;
HeroNode2 temp=head.next;
while (true){
if (temp==null){
break;
}
if (temp.num==newheronode.num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
temp.name=newheronode.name;
temp.nickname=newheronode.nickname;
}else {
System.out.printf("没有找到编号为%d的节点\n",newheronode.num);
}
}
//删除节点
public void del(int num){
if (head.next==null){
System.out.println("链表为空,无法删除");
return;
}
HeroNode2 temp=head.next;
boolean flag=false; //是否找到
while (true){
if (temp==null){
break;
}
if (temp.num==num){
flag=true;
break;
}
temp=temp.next;
}
if (flag){
//删除
temp.pre.next=temp.next;
//如果是最后一个节点 不需要指向
if(temp.next!=null){
temp.next.pre=temp.pre;
}
}else {
System.out.printf("没有找到要删除的编号为%d的节点\n",num);
}
}
}
class HeroNode2{
public int num;
public String name;
public String nickname;
public HeroNode2 next;
public HeroNode2 pre;
public HeroNode2 (int num,String name,String nickname){
this.num=num;
this.name=name;
this.nickname=nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"num=" + num +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
栈
先入后出 (前缀 中缀 后缀)
class ArrayStack{
private int maxsize;
private int[] stack;
private int top=-1;
public ArrayStack(int maxsize){
this.maxsize=maxsize;
stack=new int[this.maxsize];
}
public boolean isFull(){
return top==maxsize-1;
}
public boolean isEmpty(){
return top==-1;
}
public void push(int value){
if(isFull()){
System.out.println("栈满");
return;
}
top++;
stack[top]=value;
}
public int pop(){
if (isEmpty()){
throw new RuntimeException("栈空");
}
int value=stack[top];
top--;
return value;
}
//遍历
public void list(){
if (isEmpty()){
System.out.println("栈空");
return;
}
for (int i=top;i>=0;i--){
System.out.printf("stack[%d]=%d\n",i,stack[i]);
}
}
}
递归
调用方法:开辟独立的空间栈
迷宫回溯
package recursion;
/**
* @project_name: DateConstruct
* @description:
* @author: ZZW
* @time: 2019/11/27 9:55
*/
public class Migong {
public static void main(String[] args) {
int [][] map=new int[8][7] ;
// 使用1 表示墙
// 上下全部置为1
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
// 左右全部置为1
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
//设置挡板, 1 表示
map[3][1] = 1;
map[3][2] = 1;
map[4][3] = 1;
//map[1][2] = 1;
map[2][2] = 1;
// 输出地图
System.out.println("地图的情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
//使用递归回溯给小球找路
setway(map, 1, 1);
System.out.println("小球走过,并标识过的 地图的情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
//使用递归回溯来给小球找路
//说明
//1. map 表示地图
//2. i,j 表示从地图的哪个位置开始出发 (1,1)
//3. 如果小球能到 map[6][5] 位置,则说明通路找到.
//4. 约定: 当map[i][j] 为 0 表示该点没有走过 当为 1 表示墙 ; 2 表示通路可以走 ; 3 表示该点已经走过,但是走不通
//5. 在走迷宫时,需要确定一个策略(方法) 下->右->上->左 , 如果该点走不通,再回溯
/**
*
* @param map 表示地图
* @param i 从哪个位置开始找
* @param j
* @return 如果找到通路,就返回true, 否则返回false
*/
public static boolean setway(int[][] map,int i,int j){
if(map[6][5] == 2) { // 通路已经找到ok
return true;
}
else if(map[i][j]==0){//如果当前这个点还没有走过
//按照策略 下->右->上->左 走
map[i][j]=2;
if (setway(map,i+1,j)){
return true;
}
else if (setway(map, i, j+1)) { //向右走
return true;
}
else if (setway(map, i-1, j)) { //向上
return true;
}
else if (setway(map, i, j-1)){ // 向左走
return true;
}
else {
map[i][j]=3;
return false;
}
}
else {
return false;
}
}
//修改找路的策略,改成 上->右->下->左
public static boolean setWay2(int[][] map, int i, int j) {
if(map[6][5] == 2) { // 通路已经找到ok
return true;
} else {
if(map[i][j] == 0) { //如果当前这个点还没有走过
//按照策略 上->右->下->左
map[i][j] = 2; // 假定该点是可以走通.
if(setWay2(map, i-1, j)) {//向上走
return true;
} else if (setWay2(map, i, j+1)) { //向右走
return true;
} else if (setWay2(map, i+1, j)) { //向下
return true;
} else if (setWay2(map, i, j-1)){ // 向左走
return true;
} else {
//说明该点是走不通,是死路
map[i][j] = 3;
return false;
}
} else { // 如果map[i][j] != 0 , 可能是 1, 2, 3
return false;
}
}
}
}
八皇后问题
package recursion;
/**
* @project_name: DateConstruct
* @description:
* @author: ZZW
* @time: 2019/11/27 11:11
*/
public class Queue8 {
//定义一个max表示共有多少个皇后
int max = 8;
//定义数组array, 保存皇后放置位置的结果,比如 arr = {0 , 4, 7, 5, 2, 6, 1, 3}
int[] array = new int[max];
static int count = 0;
static int judgeCount = 0;
public static void main(String[] args) {
//测试一把 , 8皇后是否正确
Queue8 queue8 = new Queue8();
queue8.check(0);
System.out.printf("一共有%d解法", count);
System.out.printf("一共判断冲突的次数%d次", judgeCount); // 1.5w
}
//编写一个方法,放置第n个皇后
//特别注意: check 是 每一次递归时,进入到check中都有 for(int i = 0; i < max; i++),因此会有回溯
private void check(int n) {
if(n == max) { //n = 8 , 其实8个皇后就既然放好
print();
return;
}
//依次放入皇后,并判断是否冲突
for(int i = 0; i < max; i++) {
//先把当前这个皇后 n , 放到该行的第1列
array[n] = i;
//判断当放置第n个皇后到i列时,是否冲突
if(judge(n)) { // 不冲突
//接着放n+1个皇后,即开始递归
check(n+1); //
}
//如果冲突,就继续执行 array[n] = i; 即将第n个皇后,放置在本行得 后移的一个位置
}
}
//查看当我们放置第n个皇后, 就去检测该皇后是否和前面已经摆放的皇后冲突
/**
*
* @param n 表示第n个皇后
* @return
*/
private boolean judge(int n) {
judgeCount++;
for(int i = 0; i < n; i++) {
// 说明
//1. array[i] == array[n] 表示判断 第n个皇后是否和前面的n-1个皇后在同一列
//2. Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判断第n个皇后是否和第i皇后是否在同一斜线
// n = 1 放置第 2列 1 n = 1 array[1] = 1
// Math.abs(1-0) == 1 Math.abs(array[n] - array[i]) = Math.abs(1-0) = 1
//3. 判断是否在同一行, 没有必要,n 每次都在递增
if(array[i] == array[n] || Math.abs(n-i) == Math.abs(array[n] - array[i]) ) {
return false;
}
}
return true;
}
//写一个方法,可以将皇后摆放的位置输出
private void print() {
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}
排序
冒泡排序
//8万数据9秒
public static void bubbleSort(int[] arr){
// 冒泡排序 的时间复杂度 O(n^2), 自己写出
int temp=0;
boolean flag=false; //标识变量,表示是否进行过交换
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]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
// System.out.println("第" + (i + 1) + "趟排序后的数组");
// System.out.println(Arrays.toString(arr));
if (!flag) { // 在一趟排序中,一次交换都没有发生过
break;
} else {
flag = false; // 重置flag!!!, 进行下次判断
}
}
}
选择排序
//创建要给80000个的随机的数组 2-3秒
//选择排序
public static void selectSort(int[] arr){
//选择排序时间复杂度是 O(n^2)
for (int i=0;i<arr.length-1;i++){
int minIndex=i;
int min=arr[i];
for (int j=i+1;j<arr.length;j++){
if (min>arr[j]){
min=arr[j];
minIndex=j;
}
}
if (minIndex!=i){
arr[minIndex]=arr[i];
arr[i]=min;
}
}
}
插入排序
8万数据不到1秒
public static void insertSort(int [] arr){
int insertVal=0;
int insertIndex=0;
for (int i=1;i<arr.length;i++){
insertVal=arr[i];
insertIndex=i-1;// 即arr[1]的前面这个数的下标
// 给insertVal 找到插入的位置
// 说明
// 1. insertIndex >= 0 保证在给insertVal 找插入位置,不越界
// 2. insertVal < arr[insertIndex] 待插入的数,还没有找到插入位置
// 3. 就需要将 arr[insertIndex] 后移
while (insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+1]=arr[insertIndex];
insertIndex--;
}
if(insertIndex+1!=i){
arr[insertIndex+1]=insertVal;
}
}
}
希尔排序
8万数据 交换式8S 移动式不到1S
//交换式
public static void shellsort(int [] arr){
int temp=0;
int count=0;
for (int gap=arr.length/2;gap>0;gap/=2) {
for (int i = gap; i < arr.length; i++)
// 遍历各组中所有的元素(共gap组,每组有个元素), 步长gap
for (int j = i - gap; j >= 0; j -= gap) {
// 如果当前元素大于加上步长后的那个元素,说明交换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
//System.out.println("希尔排序第" + (++count) + "轮 =" + Arrays.toString(arr));
}
}
//移动式
public static void shellsort2(int [] arr){
for (int gap=arr.length/2;gap>0;gap/=2) {
for (int i = gap; i < arr.length; i++){
// 从第gap个元素,逐个对其所在的组进行直接插入排序
int j=i;
int temp=arr[j];
//if (arr[j]<arr[j-gap])
{
while (j - gap >= 0 && temp < arr[j - gap]) {
//移动
arr[j] = arr[j-gap];
j -= gap;
}
//当退出while后,就给temp找到插入的位置
arr[j] = temp;
}
}
//System.out.println("希尔排序第" + (++count) + "轮 =" + Arrays.toString(arr));
}
}
快速排序
80万数据1-2S
public static void quicksort(int [] array,int start,int end) {
if (start>end) return;
int l=start;
int r=end;
int value=array[end];
while(l<r) {
while(l<r&&value>=array[l])
l++;
while(l<r&&value<=array[r])
r--;
if (l>=r) break;
int tem=array[l];
array[l]=array[r];
array[r]=tem;
}
array[end]=array[l];
array[l]=value;
quicksort(array,start,l-1);
quicksort(array,l+1,end);
}
归并排序
80万数据1S
//合并的方法
/**
*
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp){
int i=left;
int j=mid+1;
int t=0; // 指向temp数组的当前索引
//(一)
//先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
while (i<=mid&&j<=right){
if(arr[i]<=arr[j]){
temp[t]=arr[i];
t+=1;
i+=1;
}else {
temp[t]=arr[j];
t+=1;
j+=1;
}
}
//(二)
//把有剩余数据的一边的数据依次全部填充到temp
while (i<=mid){
temp[t]=arr[i];
t+=1;
i+=1;
}
while (j<=right){
temp[t]=arr[j];
t+=1;
j+=1;
}
//(三)
//将temp数组的元素拷贝到arr
//注意,并不是每次都拷贝所有
t=0;
int tempLeft=left;
while (tempLeft<=right){
arr[tempLeft]=temp[t];
t+=1;
tempLeft+=1;
}
}
基数排序
800万数据1s
public static void radixsort(int[] arr){
int max=arr[0];
for (int i=1;i<arr.length;i++){
if (arr[i]>max){
max=arr[i];
}
}
int maxLenght=(max+"").length();
//定义一个二维数组,表示10个桶, 每个桶就是一个一维数组
//说明
//1. 二维数组包含10个一维数组
//2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length
//3. 名明确,基数排序是使用空间换时间的经典算法
int[][] bucket=new int[10][arr.length];
//为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
//可以这里理解
//比如:bucketElementCounts[0] , 记录的就是 bucket[0] 桶的放入数据个数
int[] bucketElementCounts = new int[10];
for (int i=0,n=1;i<maxLenght;i++,n*=10){
for (int j=0;j<arr.length;j++){
int digitOfelement=arr[j]/n%10;
bucket[digitOfelement][bucketElementCounts[digitOfelement]]=arr[j];
bucketElementCounts[digitOfelement]++;
}
int index=0;
for (int k=0;k<bucketElementCounts.length;k++){
if(bucketElementCounts[k]!=0){
for (int l=0;l<bucketElementCounts[k];l++){
arr[index++]=bucket[k][l];
}
}
//第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!!
bucketElementCounts[k]=0;
}
}
}
堆排序
800万数据2S
public static void heapsort(int arr[]){
int temp=0;
System.out.println("堆排序");
// adjustHeap(arr, 1, arr.length);
// System.out.println("第一次" + Arrays.toString(arr)); // 4, 9, 8, 5, 6
//完成我们最终代码
//将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
for(int i = arr.length / 2 -1; i >=0; i--) {
adjustHeap(arr, i, arr.length);
}
/*
* 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
*/
for(int j = arr.length-1;j >0; j--) {
//交换
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, j);
}
//System.out.println("数组=" + Arrays.toString(arr));
}
//将一个数组(二叉树), 调整成一个大顶堆
/**
* 功能: 完成 将 以 i 对应的非叶子结点的树调整成大顶堆
* 举例 int arr[] = {4, 6, 8, 5, 9}; => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
* 如果我们再次调用 adjustHeap 传入的是 i = 0 => 得到 {4, 9, 8, 5, 6} => {9,6,8,5, 4}
* @param arr 待调整的数组
* @param i 表示非叶子结点在数组中索引
* @param lenght 表示对多少个元素继续调整, length 是在逐渐的减少
*/
public static void adjustHeap(int arr[], int i, int lenght){
int temp = arr[i];//先取出当前元素的值,保存在临时变量
//开始调整
//说明
//1. k = i * 2 + 1 k 是 i结点的左子结点
for (int k=i*2+1; k <lenght ; k=k*2+1) {
if (k+1<lenght&&arr[k] < arr[k+1])
k++;
if (arr[k]>temp){
arr[i]=arr[k];
i=k;
}
else{
break;
}
}
arr[i]=temp;
}
查找
线性查找
/**
* 这里我们实现的线性查找是找到一个满足条件的值,就返回
* @param arr
* @param value
* @return
*/
public static int seqsearch(int[] arr,int value){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == value) {
return i;
}
}
return -1;
}
二分查找
/**
* 课后思考题: {1,8, 10, 89, 1000, 1000,1234} 当一个有序数组中,
* 有多个相同的数值时,如何将所有的数值都查找到,比如这里的 1000
* <p>
* 思路分析
* 1. 在找到mid 索引值,不要马上返回
* 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
* 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
* 4. 将Arraylist返回
*/
public static List<Integer> binarySearch2(int[] arr, int left, int right, int findVal) {
//System.out.println("hello~");
// 当 left > right 时,说明递归整个数组,但是没有找到
if (left > right) {
return new ArrayList<Integer>();
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findVal > midVal) { // 向 右递归
return binarySearch2(arr, mid + 1, right, findVal);
} else if (findVal < midVal) { // 向左递归
return binarySearch2(arr, left, mid - 1, findVal);
} else {
// * 思路分析
// * 1. 在找到mid 索引值,不要马上返回
// * 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
// * 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
// * 4. 将Arraylist返回
List<Integer> resIndexlist = new ArrayList<Integer>();
int temp=mid-1;
while (true){
if (temp<0||arr[temp]!=findVal)
break;
resIndexlist.add(temp);
temp-=1;
}
resIndexlist.add(mid);
temp=mid+1;
while (true){
if (temp>arr.length-1||arr[temp]!=findVal)
break;
resIndexlist.add(temp);
temp+=1;
}
return resIndexlist;
}
}
插入查找
//编写插值查找算法
//说明:插值查找算法,也要求数组是有序的
/**
*
* @param arr 数组
* @param left 左边索引
* @param right 右边索引
* @param findVal 查找值
* @return 如果找到,就返回对应的下标,如果没有找到,返回-1
*/
public static int insertValueSearch(int[] arr, int left, int right, int findVal) {
System.out.println("插值查找次数~~");
//注意:findVal < arr[0] 和 findVal > arr[arr.length - 1] 必须需要
//否则我们得到的 mid 可能越界
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 insertValueSearch(arr, mid + 1, right, findVal);
} else if (findVal < midVal) { // 说明向左递归查找
return insertValueSearch(arr, left, mid - 1, findVal);
} else {
return mid;
}
}
斐波那契查找
//因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
//非递归方法得到一个斐波那契数列
public static int[] fib() {
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;
}
public static int fibSearch(int[] a,int key){
int low = 0;
int high = a.length - 1;
int k = 0; //表示斐波那契分割数值的下标
int mid = 0; //存放mid值
int f[] = fib(); //获取到斐波那契数列
//获取到斐波那契分割数值的下标
while(high > f[k] - 1) {
k++;
}
//因为 f[k] 值 可能大于 a 的 长度,因此我们需要使用Arrays类,构造一个新的数组,并指向temp[]
//不足的部分会使用0填充
int[] temp = Arrays.copyOf(a, f[k]);
//实际上需求使用a数组最后的数填充 temp
//举例:
//temp = {1,8, 10, 89, 1000, 1234, 0, 0} => {1,8, 10, 89, 1000, 1234, 1234, 1234,}
for(int i = high + 1; i < temp.length; i++) {
temp[i] = a[high];
}
// 使用while来循环处理,找到我们的数 key
while (low <= high) { // 只要这个条件满足,就可以找
mid = low + f[k - 1] - 1;
if(key < temp[mid]) { //我们应该继续向数组的前面查找(左边)
high = mid - 1;
//为甚是 k--
//说明
//1. 全部元素 = 前面的元素 + 后边元素
//2. f[k] = f[k-1] + f[k-2]
//因为 前面有 f[k-1]个元素,所以可以继续拆分 f[k-1] = f[k-2] + f[k-3]
//即 在 f[k-1] 的前面继续查找 k--
//即下次循环 mid = f[k-1-1]-1
k--;
} else if ( key > temp[mid]) { // 我们应该继续向数组的后面查找(右边)
low = mid + 1;
//为什么是k -=2
//说明
//1. 全部元素 = 前面的元素 + 后边元素
//2. f[k] = f[k-1] + f[k-2]
//3. 因为后面我们有f[k-2] 所以可以继续拆分 f[k-1] = f[k-3] + f[k-4]
//4. 即在f[k-2] 的前面进行查找 k -=2
//5. 即下次循环 mid = f[k - 1 - 2] - 1
k -= 2;
} else { //找到
//需要确定,返回的是哪个下标
if(mid <= high) {
return mid;
} else {
return high;
}
}
}
return -1;
}