数据结构1

目录

概念

数据结构概念

四种基本机构

稀疏数组

队列

链表

递归

迷宫回溯

八皇后问题

排序

冒泡排序

选择排序

插入排序

希尔排序

快速排序

归并排序

基数排序

堆排序

查找

线性查找

二分查找

插入查找

斐波那契查找

 


概念

  • 数据结构概念

数据的逻辑结构  

线性结构: 线性表 栈 队列

非线性结构:  树形结构 图形结构

数据的储存结构  顺序储存、链式储存

数据的运算  检索、排序、插入、删除、修改等

  • 四种基本机构

集合 线性结构 树形结构 图状结构或网状

稀疏数组

队列

有序列表  先入先出

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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值