数据结构与算法

1.稀疏数组

/**
 * 将稀疏数组进行压缩
 * 处理方法:
 *      1. 记录数组一共有几行几列,有多少个不同的值。
 *      2. 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模。
 * 
 * 例子:
 * 
 * 稀疏数组:
 * 0    0   0   22  0   0   15
 * 0    11  0   0   0   17  0
 * 0    0   0   -6  0   0   0
 * 0    0   0   0   0   39  0       
 * 91   0   0   0   0   0   0
 * 0    0   28  0   0   0   0
 * 
 * 压缩后:
 * 行   列  值
 * 6    7   8   // 一共有几行几列以及几个非0值
 * 0    3   22
 * 0    6   15
 * 1    1   11
 * 1    5   17
 * 2    3   -6
 * 3    5   39
 * 4    0   91
 * 5    2   28
 */
public class SparseArray {
    public static void main(String[] args) {
        //创建一个二维数组
        int[][] charArray = new int[11][11];
        charArray[1][2] = 1;
        charArray[2][3] = 2;

        //输出原始的数组
        System.out.println("原始的二维数组:");
        for (int[] row : charArray) {
            for(int data : row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }

        //统计一共有多少个非0元素
        int sum = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if(charArray[i][j] != 0){
                    sum++;
                }
            }
        }
        
        //压缩数组
        int[][] sparseArr = new int[sum + 1][3];
        sparseArr[0][0] = 11;
        sparseArr[0][1] = 11;
        sparseArr[0][2] = sum;
        int count = 0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if(charArray[i][j] != 0){
                    count++;
                    sparseArr[count][0] = i;
                    sparseArr[count][1] = j;
                    sparseArr[count][2] = charArray[i][j];
                }
            }
        }

        //输出压缩数组
        System.out.println();
        System.out.println("转换后的稀疏数组为:");
        for (int i = 0; i < sparseArr.length; i++) {
            System.out.printf("%d\t%d\t%d\t\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);            
        }
        System.out.println();

        //恢复二维数组
        int[][] charArray2 = new int[sparseArr[0][0]][sparseArr[0][1]];
        for (int i = 1; i < sparseArr.length; i++) {
            charArray2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
        }
        System.out.println("恢复后的二维数组:");
        for (int[] row : charArray2) {
            for(int data : row){
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }
    }
}

2.队列

import java.util.Scanner;

/**
 * 环形队列
 * 实现思路:
 * 1. front指向队列的第一个元素。初始值front=0;
 * 2. rear指向队列的最后一个元素的后一个位置。希望空出一个空间做约定。初始值reaer=0。
 * 3. 队列满的条件:(rear + 1) % maxSize = front
 * 4. 队列空的条件:rear == front
 * 5. 队列中的有效数据的个数:(rear + maxSize - front) % maxSize
 */
public class ArrayQueue {
    public static void main(String[] args) {
        Queue queue = new Queue(3);
        char key = ' ';
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        while(loop){
            System.out.println("s:显示队列");
            System.out.println("a:添加数据");
            System.out.println("g:取出数据");
            System.out.println("h:查头数据");
            System.out.println("e:退出程序");
            key = scanner.next().charAt(0);
            switch (key) {
                case 's':
                    queue.showQueue();
                    break;
                case 'a':
                    System.out.println("请输入一个数:");
                    int value = scanner.nextInt();
                    queue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = queue.getQueue();
                        System.out.printf("取出的数据是:%d", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int head = queue.headQueue();
                        System.out.printf("队列的头数据是:%d", head);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
            System.out.println("程序退出!");
        }
    }
}

//使用数组模拟队列
class Queue{
    private int maxSize; //最大容量
    private int front;  // 队列头
    private int rear;   //队列尾
    private int[] arr;  //用于存放数据

    //创建队列的构造器
    public Queue(int arrMaxSize){
        maxSize = arrMaxSize;
        arr = new int[arrMaxSize];
        front = 0;
        rear = 0;
    }

    //判断队列是否满
    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 + 1) % maxSize;
    }

    //数据出队列
    public int getQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列空,不能取数据");
        }
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }

    //显式队列的所有数据
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列为空...");
            return;
        }
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }
    }

    //求出队列有效数据个数
    public int size(){
        return (rear + maxSize - front) % maxSize;
    }

    //显式队列的头数据
    public int headQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列为空...");
        }
        return arr[front];
    }
}

3.链表

3.1单向链表

/**
 * 单向链表的实现
 */
public class SingleLinkedList {
    public static void main(String[] args) {
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
        LinkedList linkedList = new LinkedList();
        linkedList.add(hero1);
        linkedList.add(hero2);
        linkedList.add(hero3);
        linkedList.add(hero4);
        linkedList.list();
    }
}

class LinkedList{
    private HeroNode head = new HeroNode(0, "", "");
    private HeroNode tail = head;

    //添加操作
    public void add(HeroNode heroNode){
        tail.next = heroNode;
        tail = tail.next;
    }

    //按照顺序添加
    public void addByOrder(HeroNode heroNode){
        HeroNode temp = head;
        boolean flag = false; // 是否已经存在此编号
        while(true){
            if(temp.next == null){
                break;
            }
            if(temp.next.no > heroNode.no){
                break;
            }else if(temp.next.no == heroNode.no){ // 编号已经存在
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            System.out.printf("准备插入的英雄编号%d已经存在了", heroNode.no);
        }else{
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    //显示链表
    public void list(){
        if(head.next == null){
            System.out.println("俩表为空");
            return;
        }
        HeroNode temp = head.next;
        while(true){
            if(temp == null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

    //根据编号修改信息
    public void update(HeroNode heroNode){
        if(head.next == null){
            System.out.println("链表为空..");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false;//表示是否找到该节点
        while(true){
            if(temp == null){ // 链表已经遍历完毕
                break;
            }
            if(temp.no == heroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = heroNode.name;
            temp.nickName = heroNode.nickName;
        }else{
            System.out.printf("未找到该编号%d", heroNode.no);
        }
    }

    //删除节点
    public void delete(int no){
        HeroNode temp = head;
        boolean flag = false;
        while(true){
            if(temp.next == null){
                break;
            }
            if(temp.next.no == no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.next = temp.next.next;
        }else{
            System.out.printf("在链表中不存在编号%d", no);
        }
    }
}

class HeroNode{
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;

    public HeroNode(int no, String name, String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode[no=" + no + ",name=" + name + ",nickName=" + nickName + "]";
    }
}

3.2双向链表

/**
 * 双向链表的实现
 */
public class DoubleLinkedList {
    public static void main(String[] args) {
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
        LinkedList linkedList = new LinkedList();
        linkedList.add(hero1);
        linkedList.add(hero2);
        linkedList.add(hero3);
        linkedList.add(hero4);
        linkedList.list();
    }
}

class LinkedList{
    private HeroNode head = new HeroNode(0, "", "");
    private HeroNode tail = head;

    //添加操作
    public void add(HeroNode heroNode){
        tail.next = heroNode;
        heroNode.pre = tail;
    }

    //显示链表
    public void list(){
        if(head.next == null){
            System.out.println("俩表为空");
            return;
        }
        HeroNode temp = head.next;
        while(true){
            if(temp == null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

    //根据编号修改信息
    public void update(HeroNode heroNode){
        if(head.next == null){
            System.out.println("链表为空..");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false;//表示是否找到该节点
        while(true){
            if(temp == null){ // 链表已经遍历完毕
                break;
            }
            if(temp.no == heroNode.no){
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = heroNode.name;
            temp.nickName = heroNode.nickName;
        }else{
            System.out.printf("未找到该编号%d", heroNode.no);
        }
    }

    //删除节点
    public void delete(int no){
        if(head.next == null){
            System.out.println("链表为空,无法删除");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false;
        while(true){
            if(temp == null){
                break;
            }
            if(temp.no == no){
                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", no);
        }
    }
}

class HeroNode{
    public int no;
    public String name;
    public String nickName;
    public HeroNode next;
    public HeroNode pre;

    public HeroNode(int no, String name, String nickName){
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode[no=" + no + ",name=" + name + ",nickName=" + nickName + "]";
    }
}

3.3环形单向链表

/**
 * 环形单向链表的实现
 */
public class CircleLinkedList {
    public static void main(String[] args) {
        LinkedList_circle linkedList = new LinkedList_circle();
        linkedList.add(5);
        linkedList.show();
    }
}

class LinkedList_circle{
    private Boy first = null;

    //添加
    public void add(int nums){
        if(nums < 1){
            System.out.println("nums的值不正确");
            return;
        }
        Boy currentBoy = null;
        for (int i = 1; i <= nums; i++) {
            Boy boy = new Boy(i);
            if(i == 1){
                first = boy;
                first.setNext(first);
                currentBoy = first;
            }else{
                currentBoy.setNext(boy);
                boy.setNext(first);
                currentBoy = boy;
            }            
        }
    }

    //遍历
    public void show(){
        if(first == null){
            System.out.println("没有任何boy");
            return;
        }
        Boy curBoy = first;
        while(true){
            System.out.printf("小孩的编号%d\n", curBoy.getNo());
            if(curBoy.getNext() == first){
                break;
            }
            curBoy = curBoy.getNext();
        }
    }
}

//创建一个Boy类
class Boy{
    private int no;
    private Boy next;
    public Boy(int no){
        this.no = no;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public void setNext(Boy next) {
        this.next = next;
    }
    public Boy getNext() {
        return next;
    }
}

4.栈

import java.util.Scanner;

import javax.lang.model.element.Element;

/**
 * 栈的实现
 */
public class ArrayStack {
    public static void main(String[] args) {
        /**
        Stack_Array stack = new Stack_Array(4);
        String key = "";
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);    
        while(loop){
            System.out.println("show:显示栈");
            System.out.println("push:入栈");
            System.out.println("pop:出栈");
            System.out.println("exit:退出");
            key = scanner.next();
            switch (key) {
                case "show":
                    stack.show();
                    break;
                case "push":
                    System.out.println("请输入一个数:");
                    int value = scanner.nextInt();
                    stack.push(value);
                    break;
                case "pop":
                    try {
                        int res = stack.pop();
                        System.out.printf("出栈的数据是:%d", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case "exit":
                    scanner.close();
                    loop = false;
                    break;
                default:
                    System.out.println("指令有误.");
                    break;
            }
        }
         */
        
        //实现计算器
        String expression = "3+2*6-2";
        //创建两个栈,一个数栈一个符号栈
        Stack_Array numStack = new Stack_Array(10);
        Stack_Array operStack = new Stack_Array(10);
        int index = 0;
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';
        String keepNum = "";//用于拼接多位数
        while(true){
            ch = expression.substring(index, index + 1).charAt(0);
            if(operStack.isOper(ch)){
                if(!operStack.isEmpty()){
                    if(operStack.priority(ch) <= operStack.priority(operStack.peek())){
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1, num2, oper);
                        numStack.push(res);
                        operStack.push(ch);
                    }
                }else{
                    operStack.push(ch);
                }
            }else{
                //numStack.push(ch - 48);
                //处理多位数
                keepNum += ch;
                if(index == expression.length() - 1){
                    numStack.push(Integer.parseInt(keepNum));
                }else{
                    if(operStack.isOper(expression.substring(index+1, index+2).charAt(0))){
                        numStack.push(Integer.parseInt(keepNum));
                        keepNum = "";
                    }
                }
            }
            index++;
            if(index >= expression.length()){
                break;
            }
        }

        while(true){
            if(operStack.isEmpty()){
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }
        System.out.printf("表达式%s = %d", expression, numStack.pop());
    }
}

class Stack_Array{
    private int maxSize;  //最大容量
    private int[] stack;  //数组模拟栈
    private int top = -1; //栈顶,初始化为-1

    public Stack_Array(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 show(){
        if(isEmpty()){
            System.out.println("栈空.");
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    //运算符等级
    public int priority(int oper){
        if(oper == '*' || oper == '/'){
            return 1;
        }else if(oper == '+' || oper == '-'){
            return 0;
        }else{
            return -1;
        }
    }

    //判断是不是一个运算符
    public boolean isOper(char val){
        return val == '+' || val == '-' || val == '*' || val == '/';
    }

    //计算方法
    public int cal(int num1, int num2, int oper){
        int res = 0;
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }

    //返回栈顶的值
    public int peek(){
        return stack[top];
    }
}

5.排序算法

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
冒泡排序O(n^2)O(n)O(n^2)O(1)In-place稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)In-place不稳定
插入排序O(n^2)O(n^2)O(n^2)O(1)In-place稳定
希尔排序O(nlogn)O(nlog2(n))O(nlog2(n))O(1)In-place不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)Out-place稳定
快速排序O(nlogn)O(nlogn)O(n^2)O(logn)In-place不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)In-place不稳定
计数排序O(n + k)O(n + k)O(n + k)O(k)Out-place稳定
桶排序O(n + k)O(n + k)O(n^2)O(n + k)Out-place稳定
基数排序O(n x k)O(n x k)O(n x k)O(n + k)Out-place稳定

5.1冒泡排序

import java.util.Arrays;

/**
 * 冒泡排序的实现
 * 
 * 通过对排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,
 * 使值较大的元素逐渐从前向后移动。
 */
public class BubbleSort {
    public static void main(String[] args) {
        int arr[] = {3, 9, -1, 10, -2};
        System.out.println("冒泡排序前数组:" + Arrays.toString(arr));
        bubbleSort(arr);
        System.out.println("冒泡排序后数组:" + Arrays.toString(arr));
    }

    public static void bubbleSort(int [] arr){
        int temp = 0;
        boolean flag = false;
        for (int i = 0; i < arr.length; 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;
                }
            }
            if(!flag){
                break;
            }else{
                flag = false;
            }
        }
    }
}

5.2选择排序

import java.util.Arrays;

/**
 * 选择排序
 * 
 * 第一次从arr[0]~arr[n-1]选取最小值,与arr[0]交换
 * 第二次从arr[1]~arr[n-1]选取最小值,与arr[1]交换
 * ....
 */
public class SelectSort {
    public static void main(String[] args) {
        int[] arr = {101, 34, 119, 1};
        System.out.println("选择排序之前:" + Arrays.toString(arr));
        selectSort(arr);
        System.out.println("选择排序之后:" + Arrays.toString(arr));
    }

    public static void selectSort(int[] arr){
        int min = 0;
        int index;
        for (int i = 0; i < arr.length; i++) {
            min = arr[i];
            index = i;
            for (int j = i + 1; j < arr.length; j++) {
                if(min > arr[j]){
                    min = arr[j];
                    index = j;
                }
            }
            arr[index] = arr[i];
            arr[i] = min;
        }
    }
}

5.3插入排序

import java.util.Arrays;

/**
 * 插入排序
 * 
 * 把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表只包含一个元素
 * 无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排
 * 序码依次与有序元素的排序码进行比较,将它插入到有序表中的适当位置,使之称为
 * 新的有序表
 * 
 * 例子:
 * 初始 (17) 3 25 14 20 9
 * 1    (3,17) 25 14 20 9
 * 2    (3, 17, 25) 14 20 9
 * 3    (3, 14, 17, 25) 20 9
 * 4    (3, 14, 17, 20, 25) 9
 * 5    (3, 9, 14, 17, 20, 25)
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {17, 3, 25, 14, 20, 9};
        System.out.println("插入排序前的数组:"+ Arrays.toString(arr));
        insertSort(arr);
        System.out.println("插入排序后的数组:"+ Arrays.toString(arr));
    }

    public static void insertSort(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            int insertVal = arr[i];
            int insertIndex = i - 1;
            while(insertIndex >= 0 && insertVal < arr[insertIndex]){
                arr[insertIndex + 1] =arr[insertIndex];
                insertIndex--;
            }
            arr[insertIndex + 1] = insertVal;
        }
    }
}

5.4希尔排序

import java.util.Arrays;

/**
 * 希尔排序
 * 
 * 把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
 * 随着增量逐渐减小,每组包含的关键词越来越多,当增量减至1时,
 * 整个文件恰被分成一组,算法便被终止。
 * 
 * 例子
 * 原始数组:8 9 1 7 2 3 5 4 6 0
 * 第一轮:
 *      分组:[8 3] [9 5] [1 4] [7 6] [2 0]
 *      排序:3 5 1 6 0 8 9 4 7 2
 * 第二轮:
 *      分组:[3 1 0 9 7] [5 6 8 4 2]
 *      排序:0 2 1 4 3 5 7 6 9 8
 * 第三轮:
 *      分组:[0 2 1 4 3 5 7 6 9 8]
 *      排序:0 1 2 3 4 5 6 7 8 9
 */
public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("排序前:"+ Arrays.toString(arr));
        shellSort2(arr);
        System.out.println("排序后:"+ Arrays.toString(arr));
    }

    //交换式希尔排序
    public static void shellSort(int[] arr){
        int temp = 0;
        for (int i = arr.length / 2; i > 0; i /= 2) {
            for (int j = i; j < arr.length; j++) {
                for (int k = j - i; k >= 0; k -= i) {
                    if(arr[k] > arr[k + i]){
                        temp = arr[k];
                        arr[k] = arr[k + i];
                        arr[k + i] = temp;
                    }
                }
            }
        }
    }

    //移位式希尔排序
    public static void shellSort2(int[] arr){
        for (int i = arr.length / 2; i > 0; i/=2) {            
            for (int k = i; k < arr.length; k++) {
                int j = k;
                int temp = arr[j];
                while(j - i >= 0 && temp < arr[j - i]){
                    arr[j] = arr[j - i];
                    j -= i;
                }
                arr[j] = temp;
            }
        }
    }
}

5.5快速排序

import java.util.Arrays;

/**
 * 快速排序
 * 
 * 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比
 * 另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排
 * 序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
 * 
 */
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {-9, 78, 0, 23, -567, 70};
        System.out.println("快速排序前数组为:" + Arrays.toString(arr));
        qucikSort(arr, 0, arr.length - 1);
        System.out.println("快速排序后数组为:" + Arrays.toString(arr));
    }

    public static void qucikSort(int[] arr, int left, int right){
        int l = left;  //左下标
        int r = right; //右下标
        int temp = 0;
        int pivot = arr[(left + right) / 2]; // 获得中轴值
        while(l < r){
            while(arr[l] < pivot){ // 找到左边比中轴值小的
                l += 1;
            }
            while(arr[r] > pivot){  // 找到右边比中轴值大的
                r -= 1;
            }
            if(l >= r){
                break;
            }
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            if(arr[l] == pivot){
                r -= 1;
            }
            if(arr[r] == pivot){
                l += 1;
            }
        }
        if(l == r){
            l += 1;
            r -= 1;
        }
        if(left < r){
            qucikSort(arr, left, r);
        }
        if(right > l){
            qucikSort(arr, l, right);
        }
    }
}

5.6归并排序

import java.util.Arrays;

/**
 * 归并排序
 * 
 * 利用归并的思想实现的排序方法,该算法采用经典的分治策略
 * 分治法将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案修补再一起,即分而治之。
 * 
 */
public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 6, 1, 3, 6, 2};
        int[] temp = new int[arr.length];
        System.out.println("归并排序前的数组是:" + Arrays.toString(arr));
        mergeSort(arr, 0, arr.length - 1, temp);
        System.out.println("归并排序后的数组是:" + Arrays.toString(arr));
    }

    public static void mergeSort(int[] arr, int left, int right, int[] temp){
        if(left < right){
            int mid = (left + right) / 2;
            //左递归分解
            mergeSort(arr, left, mid, temp);
            //右递归分解
            mergeSort(arr, mid + 1, right, temp);
            //合并
            merge(arr, left, mid, right, 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数组的索引
        
        while(i <= mid && j <= right){
            if(arr[i] <= arr[j]){
                temp[t] = arr[i];
                i++;
                t++;
            }else{
                temp[t] = arr[j];
                j++;
                t++;
            }
        }

        //左边的有剩余
        while(i <= mid){
            temp[t] = arr[i];
            i++;
            t++;
        }

        //右边有剩余
        while(j <= right){
            temp[t] = arr[j];
            j++;
            t++;
        }

        //每次不是将所有都拷贝到temp中
        t = 0;
        int tempLeft = left;
        while(tempLeft <= right){
            arr[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }
    }
}

5.7基数排序

import java.util.Arrays;

/**
 * 基数排序(桶排序)
 * 
 * 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。
 * 然后,从最低位开始,依次进行一次排序。这样从最低位排序一直
 * 到最高位排序完成以后,数列就变成一个有序序列。
 * 
 * 桶0 1 2 3 4 5 6 7 8 9
 * 第一轮:取个位数放到对应桶
 * 第二轮:取十位数放到对应桶
 * 第三轮:取千位数放到对应桶
 * ....
 */
public class RadixSort {
    public static void main(String[] args) {
        int[] arr = {53, 3, 542, 748, 14, 214};
        radixSort(arr);
    }

    public static void radixSort(int[] arr){
        //定义桶
        int[][] bucket = new int[10][arr.length];
        //记录每个桶中的数据
        int[] bucketElementCounts = new int[10];
        //计算最大的数以及它的位数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        int maxLength = (max + "").length();
        //数据放进桶里边
        for (int i = 0; i < maxLength; i++) {
            for (int j = 0; j < arr.length; j++) {
                //获取到每一位的值
                int digitOfElement = arr[j] / (int)Math.pow(10, i) % 10;
                //将该为对应的值放入到对应的桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            int index = 0;
            //将数据放入到原数组当中
            for (int k = 0; k < bucketElementCounts.length; k++) {
                if(bucketElementCounts[k] != 0){ // 桶中数据不为0
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                bucketElementCounts[k] = 0;
            }
            System.out.println("第" + (i + 1) + "轮:" + Arrays.toString(arr));
        }
    }
}

5.8堆排序

package Sort;

import java.util.Arrays;

/**
 * 堆排序
 * 基本思想:
 *  1.将待排序序列构造成一个大顶堆
 *  2.此时,整个序列的最大值就是堆顶的根节点
 *  3.将其与末尾元素进行交换,此时末尾成为最大值
 *  4.然后将剩余n-1个元素重新构成一个堆
 * 
 * 大顶堆:该节点的值大于其左右子节点的值【升序】
 * 小顶堆:该节点的值小于其左右子节点的值【降序】
 * 
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {4, 6, 8, 5, 9};
        headSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void headSort(int[] arr) {
        int temp = 0;
        System.out.println("堆排序!!!");

        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }

        for (int j = arr.length - 1; j > 0; j--) {
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            adjustHeap(arr, 0, j);
        }
    }

    //调整为大顶堆
    /**
     * 
     * @param arr 待调整数组
     * @param i 非叶子节点的索引
     * @param length 要调整元素的个数(在不断的减少)
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        int temp = arr[i];
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            if(k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            if(arr[k] > temp) {
                arr[i] = arr[k];
                i = k;
            }else {
                break;
            }
        }
        arr[i] = temp;
    }   
}

6.查找算法

6.1二分查找

package Search;
/**
 * 二分查找的前提是该数组是有序的
 */
public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        int index = binarySearch(arr, 0, arr.length, 1234);
        System.out.println("索引为:" + index);
    }

    /**
     * 
     * @param arr 数组
     * @param left 左边索引
     * @param right 右边索引
     * @param findVal 要查找的值
     * @return 找到返回下标,没找到返回-1
     */
    public static int binarySearch(int[] arr, int left, int right, int findVal){
        if(left > right){
            return -1;
        }
        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;
        }
    }
}

6.2插值查找

package Search;

/**
 * 插值查找
 * 类似于二分查找,不同的是插值查找每次从自适应mid处开始查找
 * int mid = left + (right - left) * (findVal - arr[left]) / (arr[right] - arr[left])
 * 
 * 对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找,速度较快
 * 关键字分布不均匀的情况下,该方法不一定比折半查找要好
 */
public class InsertValueSearch {
    public static void main(String[] args) {
        int[] arr = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i + 1;
        }
        int index = insertValueSearch(arr, 0, args.length, 30);
        System.out.println("索引为:" + index);
    }

    public static int insertValueSearch(int[] arr, int left, int right, int findVal){
        if(left > right || findVal < arr[0] || findVal > arr[arr.length - 1]){
            return -1;
        }
        //关键点
        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;
        }
    }
}

7.哈希表

package HashTable;

import java.util.Scanner;

import jdk.nashorn.internal.ir.EmptyNode;

/**
 * 哈希表的实现:数组+链表
 */
public class HashTabDemo {
    public static void main(String[] args) {
        HashTab hTab = new HashTab(7);
        String key = "";
        Scanner sc = new Scanner(System.in);
        while(true){
            System.out.println("add:添加雇员");
            System.out.println("list:显示雇员");
            System.out.println("exit:退出系统");
            key = sc.next();
            switch (key) {
                case "add":
                    System.out.println("输入id");
                    int id = sc.nextInt();
                    System.out.println("输入姓名");
                    String name = sc.next();
                    Emp emp = new Emp(id, name);
                    hTab.add(emp);
                    break;
                case "list":
                    hTab.list();
                    break;
                case "find":
                    System.out.println("输入查找的id");
                    id = sc.nextInt();
                    hTab.findEmpById(id);
                    break;
                case "exit":
                    sc.close();
                    System.exit(0);
                default:
                    break;
            }
        }
    }
}

//  雇员
class Emp {
    public int id;
    public String name;
    public Emp next;
    public Emp(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

//链表
class EmpLikedList {
    private 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;
    }

    //遍历雇员
    public void list(int no){
        if(head == null){
            System.out.println("第"+ (no + 1) +"链表信息为空.");
            return;
        }
        System.out.println("第"+ (no + 1) +"链表的信息为:");
        Emp curEmp = head;
        while(true){
            System.out.printf("=> id = %d, name=%s\t'", curEmp.id, curEmp.name);
            if(curEmp.next == null){
                break;
            }
            curEmp = curEmp.next;
        }
        System.out.println();
    }

    //根据id查找雇员
    public Emp findEmpById(int id){
        if(head == null){
            System.out.println("链表为空");
            return null;
        }
        Emp  curEmp = head;
        while(true){
            if(curEmp.id == id){
                break;
            }
            if(curEmp.next == null){
                curEmp = null;
                break;
            }
            curEmp = curEmp.next;
        }
        return curEmp;
    }
}

//创建hashTab
class HashTab{
    private EmpLikedList[] empLikedLists;
    private int size;

    public HashTab(int size) {
        this.size = size;
        empLikedLists = new EmpLikedList[size];
        for (int i = 0; i < size; i++) {
            empLikedLists[i] = new EmpLikedList();
        }
    }

    //添加
    public void add(Emp emp){
        int empLikedListNo = hashFun(emp.id);
        empLikedLists[empLikedListNo].add(emp);
    }

    //散列函数
    public int hashFun(int id){
        return id % size;
    }

    //遍历所有的链表
    public void list(){
        for (int i = 0; i < size; i++) {
            empLikedLists[i].list(i);
        }
    }

    //根据输入id查找雇员
    public void findEmpById(int id){
        int empLikedListNo = hashFun(id); 
        Emp emp = empLikedLists[empLikedListNo].findEmpById(id);
        if(emp != null){
            System.out.printf("在第%d找到雇员id = %d\n", (empLikedListNo + 1), id);
        }else{
            System.out.println("没有找到该雇员信息.");
        }
    }
}

8.二叉树

8.1 二叉树链表实现

package Tree;
/**
 * 二叉树的前序,中序,后序遍历及查找
 */
public class BinaryTree {
    public static void main(String[] args) {
        Tree tree = new Tree();
        HeroNode node1 = new HeroNode(1, "zhangsan");
        HeroNode node2 = new HeroNode(2, "lis");
        HeroNode node3 = new HeroNode(3, "wanger");
        HeroNode node4 = new HeroNode(4, "mazi");
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        tree.setRoot(node1);

        //前序遍历
        tree.preOrder();

        //中序遍历
        tree.infixOrder();

        //后序遍历
        tree.postOrder();
    }
}

//二叉树
class Tree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //前序遍历
    public void preOrder() {
        if(this.root != null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空.");
        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.root != null){
            this.root.infixOrder();
        }else{
            System.out.println("二叉树为空.");
        }
    }

    //后序遍历
    public void postOrder() {
        if(this.root != null){
            this.root.postOrder();
        }else{
            System.out.println("二叉树为空.");
        }
    }

    //前序查找
    public HeroNode preOrderSearch(int no){
        if(root != null){
            return root.preOrderSearch(no);
        }else{
            return null;
        }
    }
    
    //中序查找
    public HeroNode infixOrderSearch(int no){
        if(root != null){
            return root.infixOrderSearch(no);
        }else{
            return null;
        }
    }

    //后序查找
    public HeroNode postOrderSearch(int no){
        if(root != null){
            return root.postOrderSearch(no);
        }else{
            return null;
        }
    }

    //删除结点
    public void delNode(int no){
        if(root != null){
            if(root.getNo() == no){
                root = null;
            }else{
                root.delNode(no);   
            }            
        }else{
            System.out.println("空树,不能删除.");
        }
    }
}

//创建HeroNode结点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public HeroNode getLeft() {
        return left;
    }
    public void setLeft(HeroNode left) {
        this.left = left;
    }
    public HeroNode getRight() {
        return right;
    }
    public void setRight(HeroNode right) {
        this.right = right;
    }
    
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
    
    //先序遍历
    public void preOrder() {
        System.out.println(this);
        if(this.left != null){
            this.left.preOrder();
        }
        if(this.right != null){
            this.right.preOrder();
        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.left != null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if(this.right != null){
            this.right.infixOrder();
        }
    }

    //后序遍历
    public void postOrder() {
        if(this.left != null){
            this.left.postOrder();
        }
        if(this.right != null){
            this.right.postOrder();
        }
        System.out.println(this);
    }

    //前序查找
    public HeroNode preOrderSearch(int no){
        if(this.no == no){
            return this;
        }
        HeroNode node = null;
        if(this.left != null){
            node = this.left.preOrderSearch(no);
        }
        if(node != null){
            return node;
        }
        if(this.right != null){
            node = this.right.preOrderSearch(no);
        }
        return node;
    }

    //中序查找
    public HeroNode infixOrderSearch(int no){
        HeroNode node = null;
        if(this.left != null){
            node = this.left.infixOrderSearch(no);
        }
        if(node != null){
            return node;
        }
        if(this.no == no){
            return this;
        }
        if(this.right != null){
            node = this.right.infixOrderSearch(no);
        }
        return node;
    }

    //后序查找
    public HeroNode postOrderSearch(int no){
        HeroNode node = null;
        if(this.left != null){
            node = this.left.postOrderSearch(no);
        }
        if(node != null){
            return node;
        }
        if(this.right != null){
            node = this.right.postOrderSearch(no);
        }
        if(this.no == no){
            return this;
        }
        return node;
    }

    //递归删除
    public void delNode(int no){
        //左节点不为空,且左节点等于要删除的值
        if(this.left != null && this.left.no == no){
            this.left = null;
            return;
        }
        //右节点不为空,且右节点等于要删除的值
        if(this.right != null && this.right.no == no){
            this.right = null;
            return;
        }
        //递归删除
        if(this.left != null){
            this.left.delNode(no);
        }
        if(this.right != null){
            this.right.delNode(no);
        }
    }
}

8.2顺序存储二叉树

package Tree;
/**
 * 顺序存储二叉树【数组的形式】
 * 特点:
 *  1.顺序二叉树通常只考虑完全二叉树
 *  2.第n个元素的左子节点为2*n+1
 *  3.第n个元素的右子节点为2*n+2
 *  4.第n个元素的父节点为(n-1)/2
 *  5.n:表示二叉树中的第几个元素(按0开始编号)
 */
public class ArrBinaryTree {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7};
        arrTree tree = new arrTree(arr);
        tree.preOrder();
    }
}

class arrTree{
    private int[] arr;

    public arrTree(int[] arr) {
        this.arr = arr;
    }

    public void preOrder() {
        this.preOrder(0);
    }
    
    //顺序存储前序遍历
    public void preOrder(int index){
        if(arr == null || arr.length == 0){
            System.err.println("数组为空,不能遍历.");
        }
        System.out.println(arr[index]);
        if((index * 2 + 1) < arr.length){
            preOrder(2 * index + 1);
        }
        if((index * 2 + 2) < arr.length){
            preOrder(2 * index + 2);
        }
    }
}

8.3线索化二叉树

package Tree;
/**
 * 线索化二叉树的实现
 */
public class ThreadBinaryTree {
    public static void main(String[] args) {
        ThreadHeroNode root = new ThreadHeroNode(1, "tom");
        ThreadHeroNode node2 = new ThreadHeroNode(3, "jack");
        ThreadHeroNode node3 = new ThreadHeroNode(6, "smith");
        ThreadHeroNode node4 = new ThreadHeroNode(8, "mary");
        ThreadHeroNode node5 = new ThreadHeroNode(10, "king");
        ThreadHeroNode node6 = new ThreadHeroNode(14, "dim");
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        ThreadTree tree = new ThreadTree();
        tree.setRoot(root);
        tree.threadNodes();

        ThreadHeroNode leftNode = node5.getLeft();
        System.out.println("10号的前驱节点是:" + leftNode);
        ThreadHeroNode rightNode = node5.getRight();
        System.out.println("10号的后继节点是:" + rightNode);

        System.out.println("使用线索化的方式遍历线索化二叉树");
        tree.threadList();
    }
}

//二叉树
class ThreadTree {
    private ThreadHeroNode root;
    //保存前一个节点
    private ThreadHeroNode pre = null;

    public void setRoot(ThreadHeroNode root) {
        this.root = root;
    }

    public void threadNodes(){
        this.threadNodes(root);
    }
    //中序线索化二叉树
    public void threadNodes(ThreadHeroNode node){
        if(node == null){
            return;
        }
        //1.线索化左子树
        threadNodes(node.getLeft());
        //2.线索化当前节点
        if(node.getLeft() == null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        if(pre != null && pre.getRight() == null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        //3.线索化右子树
        threadNodes(node.getRight());
    }

    //遍历线索化二叉树
    public void threadList(){
        ThreadHeroNode node = root;
        while(node != null){
            while(node.getLeftType() == 0){
                node = node.getLeft();
            }
            System.out.println(node);
            while(node.getRightType() == 1){
                node = node.getRight();
                System.out.println(node);
            }
            node = node.getRight();
        }
    }
}

//创建HeroNode结点
class ThreadHeroNode {
    private int no;
    private String name;
    private ThreadHeroNode left;
    private ThreadHeroNode right;

    private int leftType;//0表示左子树,1表示前驱节点
    private int rightType;//0表示右子树,1表示后继节点

    public int getLeftType() {
        return leftType;
    }
    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }
    public int getRightType() {
        return rightType;
    }
    public void setRightType(int rightType) {
        this.rightType = rightType;
    }
    public ThreadHeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public ThreadHeroNode getLeft() {
        return left;
    }
    public void setLeft(ThreadHeroNode left) {
        this.left = left;
    }
    public ThreadHeroNode getRight() {
        return right;
    }
    public void setRight(ThreadHeroNode right) {
        this.right = right;
    }
    
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
 }

8.4二叉排序树

package Tree;

public class BinarySortTree {
    public static void main(String[] args) {
        int[] arr = {7, 3, 10, 12, 5, 1, 9};
        BST bst = new BST();
        for (int i = 0; i < arr.length; i++) {
            bst.add(new BstNode(arr[i]));
        }

        //中序遍历二叉树
        System.out.println("中序遍历二叉树");
        bst.infixOrder();
    }
}

class BST {
    private BstNode root;

    //查找要删除的结点
    public BstNode search(int value) {
        if(root == null) {
            return null;
        }else {
            return root.search(value);
        }
    }

    //查找父节点
    public BstNode searchParent(int value) {
        if(root == null) {
            return null;
        }else {
            return root.searchParent(value);
        }
    }

    //编写方法
    /**
     * 
     * @param node  传入的节点(当做二叉排序树的根节点)
     * @return  返回以Node为根节点的二叉排序树的最小节点的值
     */
    public int delRightTreeMin(BstNode node) {
        BstNode target = node;
        while(target.left != null) {
            target = target.left;
        }   
        delNode(target.value);
        return target.value;
    }

    //删除节点
    public void delNode(int value) {
        if(root == null){
            return;
        }else {
            BstNode targetNode = search(value);
            //没有找到该节点
            if(targetNode == null) {
                return;
            }
            //没有父节点
            if(root.left == null && root.right == null) {
                root = null;
                return;
            }
            //查找targetNode的父节点
            BstNode parent = searchParent(value);
            //如果要删除的节点是叶子节点
            if(targetNode.left == null && targetNode.right == null) {
                if(parent.left != null && parent.left.value == value) {
                    parent.left = null;
                }else if(parent.right != null && parent.right.value == value) {
                    parent.right = null;
                }
            }else if(targetNode.left != null && targetNode.right != null) {
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;
            }else { // 删除只有一颗子树的节点
                //删除的节点有左子节点
                if(targetNode.left != null) {
                    if(parent!=null){
                        if(parent.left.value == value) {
                            parent.left = targetNode.left;
                        }else {
                            parent.right = targetNode.left;
                        }
                    }else {
                        root = targetNode.left;
                    }
                    
                }else { //删除的节点有右子节点
                    if(parent != null){
                        if(parent.left.value == value) {
                            parent.left = targetNode.right;
                        }else {
                            parent.right = targetNode.right;
                        }
                    }else {
                        root = targetNode.right;
                    }                    
                }
            }
        }
    }

    //添加节点
    public void add(BstNode node) {
        if(root == null) {
            root = node;
        }else {
            root.add(node);
        }
    }

    //中序遍历
    public void infixOrder() {
        if(root != null) {
            root.infixOrder();
        }else {
            System.out.println("二叉排序树为空,不能遍历.");
        }
    }
}

class BstNode {
    int value;
    BstNode left;
    BstNode right;
    
    public BstNode(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "BstNode [value=" + value + "]";
    }

    //添加节点
    public void add(BstNode node) {
        if(node == null) {
            return;
        }
        if(node.value < this.value) {
            if(this.left == null) {
                this.left = node;
            }else {
                this.left.add(node);
            }
        }else {
            if(this.right == null) {
                this.right = node;
            }else {
                this.right.add(node);
            }
        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if(this.right != null) {
            this.right.infixOrder();
        }
    }

    //查找要删除的节点
    public BstNode search(int value) {
        if(value == this.value) {
            return this;
        }else if(value < this.value) {
            if(this.left == null) {
                return null;
            }
            return this.left.search(value);
        }else{
            if(this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    //查找要删除的节点的父节点
    public BstNode searchParent(int value) {
        if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
            //当前结点是要删除的结点的父结点,就返回
            return this;
        }else{
            //当前查找的值小于当前结点的值
            if(value < this.value && this.left != null) {
                return this.left.searchParent(value);
            }else if(value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            }else{
                return null;
            } 
        }
    }

}

8.5平衡二叉树

package Tree;

/**
 * 平衡二叉树:左右子节点的高度差不超过1
 */
public class AVLTree {
    public static void main(String[] args) {
        int[] arr = {4, 3, 6, 5, 7, 8};
        AVL avl = new AVL();
        for (int i = 0; i < arr.length; i++) {
            avl.add(new AvlNode(arr[i]));
        }
        System.out.println("中序遍历");
        avl.infixOrder();

        System.out.println("没有做平衡处理前树的高度:" + avl.getRoot().height());
        System.out.println("树的左子树的高度为:" + avl.getRoot().leftHeight());
        System.out.println("树的右子树的高度为:" + avl.getRoot().rightHeight());


    }
}

class AVL {
    private AvlNode root;

    public AvlNode getRoot() {
        return root;
    }

    //查找要删除的结点
    public AvlNode search(int value) {
        if(root == null) {
            return null;
        }else {
            return root.search(value);
        }
    }

    //查找父节点
    public AvlNode searchParent(int value) {
        if(root == null) {
            return null;
        }else {
            return root.searchParent(value);
        }
    }

    //编写方法
    /**
     * 
     * @param node  传入的节点(当做二叉排序树的根节点)
     * @return  返回以Node为根节点的二叉排序树的最小节点的值
     */
    public int delRightTreeMin(AvlNode node) {
        AvlNode target = node;
        while(target.left != null) {
            target = target.left;
        }   
        delNode(target.value);
        return target.value;
    }

    //删除节点
    public void delNode(int value) {
        if(root == null){
            return;
        }else {
            AvlNode targetNode = search(value);
            //没有找到该节点
            if(targetNode == null) {
                return;
            }
            //没有父节点
            if(root.left == null && root.right == null) {
                root = null;
                return;
            }
            //查找targetNode的父节点
            AvlNode parent = searchParent(value);
            //如果要删除的节点是叶子节点
            if(targetNode.left == null && targetNode.right == null) {
                if(parent.left != null && parent.left.value == value) {
                    parent.left = null;
                }else if(parent.right != null && parent.right.value == value) {
                    parent.right = null;
                }
            }else if(targetNode.left != null && targetNode.right != null) {
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;
            }else { // 删除只有一颗子树的节点
                //删除的节点有左子节点
                if(targetNode.left != null) {
                    if(parent!=null){
                        if(parent.left.value == value) {
                            parent.left = targetNode.left;
                        }else {
                            parent.right = targetNode.left;
                        }
                    }else {
                        root = targetNode.left;
                    }
                    
                }else { //删除的节点有右子节点
                    if(parent != null){
                        if(parent.left.value == value) {
                            parent.left = targetNode.right;
                        }else {
                            parent.right = targetNode.right;
                        }
                    }else {
                        root = targetNode.right;
                    }                    
                }
            }
        }
    }

    //添加节点
    public void add(AvlNode node) {
        if(root == null) {
            root = node;
        }else {
            root.add(node);
        }
    }

    //中序遍历
    public void infixOrder() {
        if(root != null) {
            root.infixOrder();
        }else {
            System.out.println("二叉排序树为空,不能遍历.");
        }
    }
}


class AvlNode {
    int value;
    AvlNode left;
    AvlNode right;
    
    public AvlNode(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "AvlNode [value=" + value + "]";
    }

    //左旋转
    private void leftRotate() {
        //创建新的节点,以当前根节点的值
        AvlNode newNode = new AvlNode(value);
        //把新节点的左子树设置成为当前结点的左子树
        newNode.left = left;
        //新节点的右子树设置成带你过去的节点的右子树的左子树
        newNode.right = right.left;
        //把当前节点的值替换成右子节点的值
        value = right.value;
        //把当前节点的右子树设置成当前节点右子树的右子树
        right = right.right;
        //把当前节点的左子树设置成新的节点
        left = newNode;
    }

    //右旋转
    private void rightRotate() {
        AvlNode newNode = new AvlNode(value);
        newNode.right = right;
        newNode.left = left.right;
        newNode.left = left.right;
        value = left.value;
        left = left.left;
    }

    //返回左子树的高度
    public int leftHeight() {
        if(left == null) {
            return 0;
        }
        return left.height();
    }

    //返回右子树的高度
    public int rightHeight() {
        if(right == null) {
            return 0;
        }
        return right.height();
    }

    //返回以该节点为根节点的树的高度
    public int height() {
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    //添加节点
    public void add(AvlNode node) {
        if(node == null) {
            return;
        }
        if(node.value < this.value) {
            if(this.left == null) {
                this.left = node;
            }else {
                this.left.add(node);
            }
        }else {
            if(this.right == null) {
                this.right = node;
            }else {
                this.right.add(node);
            }
        }
        //高度不匹配时旋转[右子树高度-左子树高度 > 1, 左旋转]
        if(rightHeight() - leftHeight() > 1) {
            if(right != null && right.leftHeight() > right.rightHeight()) {
                right.rightRotate();
                leftRotate();
            }else {
                leftRotate();
            }            
        }
        //高度不匹配时旋转[左子树高度-右子树高度 > 1, 右旋转]
        if(leftHeight() - rightHeight() > 1) {
            if(left != null && left.rightHeight() > left.leftHeight()) {
                left.leftRotate();
                rightRotate();
            }else {
                rightRotate();
            }
        }
    }

    //中序遍历
    public void infixOrder() {
        if(this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if(this.right != null) {
            this.right.infixOrder();
        }
    }

    //查找要删除的节点
    public AvlNode search(int value) {
        if(value == this.value) {
            return this;
        }else if(value < this.value) {
            if(this.left == null) {
                return null;
            }
            return this.left.search(value);
        }else{
            if(this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    //查找要删除的节点的父节点
    public AvlNode searchParent(int value) {
        if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
            //当前结点是要删除的结点的父结点,就返回
            return this;
        }else{
            //当前查找的值小于当前结点的值
            if(value < this.value && this.left != null) {
                return this.left.searchParent(value);
            }else if(value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            }else{
                return null;
            } 
        }
    }
}

9.哈夫曼树

9.1哈夫曼树构造

package Tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {13, 7, 8, 3, 29, 6, 1};
        Node root = createHuffmanTree(arr);
        preOrder(root);
    }

    public static void preOrder(Node root) {
        if(root != null) {
            root.preOrder();
        }else {
            System.out.println("空树,不能遍历.");
        }
    }

    //创建哈夫曼树
    public static Node createHuffmanTree(int[] arr){
        List<Node> nodes = new ArrayList<>();
        for (int value : arr) {
            nodes.add(new Node(value));
        }

        while(nodes.size() > 1){
            //排序
            Collections.sort(nodes);

            //取出最小的两个元素
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);

            //构建新的二叉树
            Node parent = new Node(leftNode.value + rightNode.value);
            parent.left = leftNode;
            parent.right = rightNode;

            //删除处理过的二叉树
            nodes.remove(leftNode);
            nodes.remove(rightNode);

            //将parent加入到Nodes中
            nodes.add(parent);
        }

        return nodes.get(0);
    }
}

//创建节点类
class Node implements Comparable<Node> {
    int value;
    Node left;
    Node right;

    public Node(int value){
        this.value = value;
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if(this.left != null) {
            this.left.preOrder();
        }
        if(this.right != null) {
            this.right.preOrder();
        }
    }

    @Override
    public int compareTo(Node o) {
        //升序
        return this.value - o.value;
    }

    @Override
    public String toString() {
        return "Node [value=" + value + "]";
    }
}

9.2哈夫曼树压缩和解压缩

package Tree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HuffmanCode {
    public static void main(String[] args) {
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();
        
        /**
        //获取到节点
        List<Node_Code> nodes = getNodes(contentBytes);

        //创建二叉树
        Node_Code huffmanTreeRoot = createHuffmanTree(nodes);

        //前序遍历
        huffmanTreeRoot.preOrder();

        //生成哈夫曼编码
        Map<Byte, String> huffmanCodes =  getCodes(huffmanTreeRoot);
        System.out.println("生成的哈夫曼编码表为:" + huffmanCodes);

        //将字符串转换为huffman编码
        zip(contentBytes, huffmanCodes);
         */
        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println("压缩后的结果是:" + Arrays.toString(huffmanCodeBytes));

        byte[] sourceBytes = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println("解压缩后的结果是:" + new String(sourceBytes));
    }

    static Map<Byte, String> huffmanCodes = new HashMap<>();
    static StringBuilder stringBuilder = new StringBuilder();

    /**
     * 哈夫曼编码的解码操作
     * @param huffmanCodes  之前获得的哈夫曼编码map
     * @param huffman   哈夫曼编码得到的字节数组
     * @return 原来的字符串对应的数组
     */
    private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < huffmanBytes.length; i++) {
            //判断是不是最后一个字节
            boolean flag = (i == huffmanBytes.length - 1);
            stringBuilder.append(byteToBitString(!flag, huffmanBytes[i]));
        }

        Map<String, Byte> map = new HashMap<>();
        for(Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }

        List<Byte> list = new ArrayList<>();
        for (int i = 0; i < stringBuilder.length();) {
            int count = 1;
            boolean flag = true;
            Byte b = null;
            while(flag) {
                String key = stringBuilder.substring(i, i + count);
                b = map.get(key);
                if(b == null) {
                    count++;
                }else {
                    flag = false;
                }
            }
            list.add(b);
            i += count;
        }

        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
            b[i] = list.get(i);
        }
        return b;
    }

    /**
     * 
     * @param flag 标志是否需要补高位
     * @param b 传入的byte
     * @return
     */
    private static String byteToBitString(boolean flag, byte b){
        int temp = b;
        if(flag) { //如果是正数,我们还存在补高位
            temp |= 256;
        }
        String str = Integer.toBinaryString(temp);
        if(flag) {
            return str.substring(str.length() - 8);
        }else {
            return str;
        }
    }

    /**
     * 封装起来压缩方法
     * @param bytes  原始的字符串对应的字节数组
     * @return  返回的是压缩后的字节数组
     */
    private static byte[] huffmanZip(byte[] bytes) {
        //获取到节点
        List<Node_Code> nodes = getNodes(bytes);

        //创建二叉树
        Node_Code huffmanTreeRoot = createHuffmanTree(nodes);

        //生成哈夫曼编码
        Map<Byte, String> huffmanCodes =  getCodes(huffmanTreeRoot);

        //将字符串转换为huffman编码
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }
    
    //通过编码标处理字节数组
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(huffmanCodes.get(b));
        }
        int len = (stringBuilder.length() + 7) / 8;
        byte[] by = new byte[len];
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i+=8) {
            String strByte;
            if(i + 8 > stringBuilder.length()){
                strByte = stringBuilder.substring(i);
            }else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            by[index] = (byte)Integer.parseInt(strByte, 2);
            index++;
        }
        return by;
    }

    //生成哈夫曼树对应的哈夫曼编码
    private static void getCodes(Node_Code node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        stringBuilder2.append(code);
        if(node != null) {
            if(node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder2);
                //向右递归
                getCodes(node.right, "1", stringBuilder2);
            }else {
                huffmanCodes.put(node.data, stringBuilder2.toString());
            }
        }
    }

    //重载getCodes
    private static Map<Byte, String> getCodes(Node_Code root) {
        if(root == null) {
            return null;
        }
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.right, "1", stringBuilder);
        return huffmanCodes;
    }

    //前序遍历
    private static void preOrder(Node_Code root) {
        if(root != null) {
            root.preOrder();
        }else {
            System.out.println("树空.");
        }
    }

    //获取一个node节点的list
    private static List<Node_Code> getNodes(byte[] bytes){
        ArrayList<Node_Code> nodes = new ArrayList<Node_Code>();
        Map<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if(count == null) {
                counts.put(b, 1);
            }else {
                counts.put(b, count + 1);
            }
        }

        for(Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node_Code(entry.getKey(), entry.getValue()));
        }

        return nodes;
    }

    //创建哈夫曼树
    private static Node_Code createHuffmanTree(List<Node_Code> nodes) {
        while(nodes.size() > 1) {
            Collections.sort(nodes);
            Node_Code leftNode = nodes.get(0);
            Node_Code rightNode = nodes.get(1);
            Node_Code parent = new Node_Code(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

class Node_Code implements Comparable<Node_Code> {
    Byte data; //字符
    int weight;  //字符出现的次数
    Node_Code left;
    Node_Code right;

    public Node_Code(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node_Code o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node [data=" + data + ", weight=" + weight + "]";
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if(this.left != null) {
            this.left.preOrder();
        }
        if(this.right != null) {
            this.right.preOrder();
        }
    }
}

10.图

package Graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

/**
 * 创建图
 * 
 * 深度优先遍历
 * 
 * 广度优先遍历
 */
public class Graph {
    private ArrayList<String> vertexList; // 存储顶点集合
    private int[][] edges; //存储图对应的邻接矩阵
    private int numOfEdges; //表示边的数目

    //定义数组,记录某个结点是否被访问过
    private boolean[] isVisited = new boolean[5];

    public static void main(String[] args) {
        int n = 5;
        //添加顶点
        String[] VertexValue = {"A", "B", "C", "D", "E"};
        Graph graph = new Graph(n);
        for (String value : VertexValue) {
            graph.insertVertex(value);
        }
        //添加边
        //A-B,A-C,B-C,B-D,B-E
        graph.insertEdge(0, 1, 1);
        graph.insertEdge(0, 2, 1);
        graph.insertEdge(1, 2, 1);
        graph.insertEdge(1, 3, 1);
        graph.insertEdge(1, 4, 1);
        //显示图
        graph.showGraph();

        //测试深度优先遍历
        System.out.println("深度遍历:");
        graph.dfs();

        //测试广度优先遍历
        System.out.println("广度遍历:");
        graph.bfs();
    }
    
    //构造器
    public Graph(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>();
        numOfEdges = 0;
    }

    //插入结点
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }

    //添加边
    /**
     * @param v1 第一个顶点对应的下标
     * @param v2 第二个顶点对应的下标
     * @param weight
     */
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }

    //返回结点的个数
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //得到边的个数
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //返回结点i对应的下标
    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }

    //返回v1和v2的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    //显示图矩阵
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }

    //得到第一个邻接结点的下标
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexList.size(); i++) {
            if(edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    //根据前一个邻接结点的下标来获取下一个邻接结点
    public int getNextNeighbor(int v1, int v2) {
        for (int i = v2 + 1; i < vertexList.size(); i++) {
            if(edges[v1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    //深度优先遍历算法
    public void dfs(boolean[] isVisited, int i) {
        System.out.print(getValueByIndex(i) + "->");
        isVisited[i] = true;
        int w = getFirstNeighbor(i); // 获取到第一个邻接结点
        while(w != -1) {
            if(!isVisited[w]) {
                dfs(isVisited, w);
            }
            w = getNextNeighbor(i, w);
        }
    }

    //对dfs进行重载,遍历我们所有的结点,并进行dfs
    private void dfs() {
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }

    //广度优先遍历算法
    private void bfs(boolean[] isVisited, int i) {
        int u; //表示队列的头节点对应的下标
        int w;
        LinkedList queue = new LinkedList<>();
        //访问结点,输出结点信息
        System.out.println(getValueByIndex(i) + "->");
        //标记为已访问
        isVisited[i] = true;
        queue.addLast(i);
        while(!queue.isEmpty()) {
            //取出队列头结点下标
            u = (Integer)queue.removeFirst();
            //得到第一个邻接结点的下标w
            w = getFirstNeighbor(u);
            while(w != -1) {
                if(!isVisited[w]) {
                    System.out.println(getValueByIndex(i) + "->");
                    isVisited[w] = true;
                    queue.add(w);
                }
                //以u为前驱点,找w后面的下一个邻接点
                w = getNextNeighbor(u, w);
            }
        }
    }

    //遍历所有的结点
    private void bfs() {
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
}

11.动态规划

package Dynamic;

/**
 * 利用动态规划解决0-1背包问题
 * v[i] : 第i个物品的价值
 * w[i] : 第i个物品的重量
 * C : 背包的容量
 * v[i][j] : 表示在前i个物品中能够装入容量为j的背包中的最大价值
 * (1).v[i][0] = v[0][j] = 0 //第一行第一列是0
 * (2).当w[i] > j时:v[i][j] = v[i-1][j] // 当准备加入新增的商品的容量大于当前背包的容量时,就直接使用上一个单元格的装入策略
 * (3).当j >= w[i]时:v[i][j] = max{v[i-1][j], v[i-1][j-w[i]] + v[i]} 
 * // 当准备加入新增的商品的容量大于当前背包的容量时:
 * // v[i-1][j]:上一个单元格的装入的最大值
 * // v[i]:当前商品的价值
 * // v[i-1][j-w[i]]:装入i-1商品,到剩余空间j-w[i]的最大值
 */
public class KnaspackProblem {
    public static void main(String[] args) {
        int[] w = {1, 4, 3}; //物品的重量
        int[] val = {1500, 3000, 2000}; //物品的价值
        int m = 4; // 背包的容量
        int n = val.length; // 物品的个数

        //记录放入商品的情况
        int[][] path = new int[n+1][m+1];

        //v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值
        int[][] v = new int[n + 1][m + 1];

        //初始化第一行和第一列
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0;
        }
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;
        }

        //动态规划处理
        for (int i = 1; i < v.length; i++) {
            for (int j = 1; j < v[0].length; j++) {
                if(w[i-1] > j) {
                    v[i][j] = v[i-1][j];
                }else {
                    // v[i][j] = Math.max(v[i-1][j], val[i-1]+v[i-1][j-w[i-1]]);
                    if(v[i-1][j] < val[i-1]+v[i-1][j-w[i-1]]) {
                        v[i][j] = val[i-1]+v[i-1][j-w[i-1]];
                        path[i][j] = 1;
                    }else {
                        v[i][j] = v[i-1][j];
                    }
                }
            }
        }

        //查看v的情况
        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[i].length; j++) {
                System.out.println(v[i][j] + " ");
            }
            System.out.println();
        }

        //查看放入的是哪些物品
        int i = path.length - 1; //行的最大下标
        int j = path[0].length - 1; //列的最大下标
        while(i > 0 && j > 0) {
            if(path[i][j] == 1) {
                System.out.printf("第%d个物品放入背包\n", i);
                j -= w[i-1];
            }
            i--;
        }
    }
}

12.最小生成树算法

12.1Prim算法

package Algorithm;

import java.util.Arrays;

/**
 * Prim算法:最小生成树
 */
public class PrimAlgorithm {
    public static void main(String[] args) {
        char[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int verxs = data.length;
        //10000表示不连通
        int[][] weight = new int[][] {
            {10000, 5, 7, 10000, 10000, 10000, 2},
            {5, 10000, 10000, 9, 10000, 10000, 3},
            {7, 10000, 10000, 10000, 8, 10000, 10000},
            {10000, 9, 10000, 10000, 10000, 4, 10000},
            {10000, 10000, 8, 10000, 10000, 5, 4},
            {10000, 10000, 10000, 4, 5, 10000,  6},
            {2, 3, 10000, 10000, 4, 6, 10000}
        };
        //创建MGraph对象
        MGraph mGraph = new MGraph(verxs);
        MinTree minTree = new MinTree();
        minTree.createGraph(mGraph, verxs, data, weight);
        //输出
        minTree.showGraph(mGraph);
    }
}

//创建最小生成树
class MinTree {
    //创建图的邻接矩阵
    /**
     * 
     * @param graph 图对象
     * @param verxs 图对应的顶点个数
     * @param data 图的各个顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph graph, int verxs, char[] data, int[][] weight) {
        int i, j;
        for (i = 0; i < verxs; i++) { // 顶点
            graph.data[i] = data[i];
            for ( j = 0; j < verxs; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

    //显示图的邻接矩阵
    public void showGraph(MGraph graph) {
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }

    //Prim算法
    public void prim(MGraph graph, int v) {
        //判断该节点是否被访问过
        int visited[] = new int[graph.verxs];
        for (int i = 0; i < visited.length; i++) {
            visited[i] = 0;
        }
        visited[v] = 1; // 表示已经访问过
        //h1和h2记录两个顶点的下标
        int h1 = -1;
        int h2 = -1;
        int minWeight = 10000;
        for (int k = 1; k < graph.verxs; k++) {
            for (int i = 0; i < graph.verxs; i++) {
                for (int j = 0; j < graph.verxs; j++) {
                    if(visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight){
                        minWeight = graph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }
                }
            }
            System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + ">权值:" + minWeight);
            visited[h2] = 1;
            minWeight = 10000;
        }
    }
}

class MGraph {
    int verxs; // 表示图的节点个数
    char[] data;  // 存放节点数据
    int[][] weight;  // 存放边
    
    public MGraph(int verxs) {
        this.verxs = verxs;
        data = new char[verxs];
        weight = new int[verxs][verxs];
    }
}

12.2Kruskal算法

package Algorithm;

import java.util.Arrays;

/**
 * 克鲁斯卡尔算法
 * 
 */
public class KruskalAlgorithm {

    private int edgeNum; //边的个数
    private char[] vertexs; // 顶点数组
    private int[][] matrix; // 邻接矩阵
    private static final int INF = Integer.MAX_VALUE; // 表示两个顶点不连通

    public static void main(String[] args) {
        char[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = new int[][] {
            {0, 12, INF, INF, INF, 16, 14},
            {12, 0, 10, INF, INF, 7, INF},
            {INF, 10, 0, 3, 5, 6, INF},
            {INF, INF, 3, 0, 4, INF, INF},
            {INF, INF, 5, 4, 0, 2, 8},
            {16, 7, 6, INF, 2, 0,  9},
            {14, INF, INF, INF, 8, 9, 0}
        };
        KruskalAlgorithm kruskalAlgorithm = new KruskalAlgorithm(data, matrix);
        kruskalAlgorithm.print();
        // EData[] edges = kruskalAlgorithm.getEdges();
        // System.out.println("排序前:" + Arrays.toString(edges));
        // kruskalAlgorithm.sortEdge(edges);
        // System.out.println("排序后:" + Arrays.toString(edges));
    }

    public KruskalAlgorithm(char[] vertexs, int[][] matrix){
        //初始化顶点数和边的个数
        int vlen = vertexs.length;

        //初始化顶点
        this.vertexs = new char[vlen];
        for (int i = 0; i < vertexs.length; i++) {
            this.vertexs[i] = vertexs[i];
        }
        this.vertexs = vertexs;

        //初始化边
        this.matrix = new int[vlen][vlen];
        for (int i = 0; i < vlen; i++) {
            for (int j = 0; j < vlen; j++) {
                this.matrix[i][j] = matrix[i][j];
            }
        }

        //统计边的个数
        for (int i = 0; i < vlen; i++) {
            for (int j = 0; j < vlen; j++) {
                if(this.matrix[i][j] != INF) {
                    edgeNum++;
                }
            }
        }
    }

    //打印邻接矩阵
    public void print() {
        System.out.println("邻接矩阵为:\n");
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = 0; j < vertexs.length; j++) {
                System.out.printf("%12d\t", matrix[i][j]);
            }
            System.out.println();
        }
    }

    //对边根据权值进行排序
    private void sortEdge(EData[] edges) {
        for (int i = 0; i < edges.length - 1; i++) {
            for (int j = 0; j < edges.length - 1 - i; j++) {
                if(edges[j].weight > edges[j+1].weight) {
                    EData temp = edges[j];
                    edges[j] = edges[j+1];
                    edges[j+1] = temp;
                }
            }
        }
    }

    //获取顶点对应的下标
    private int getPosition(char ch) {
        for (int i = 0; i < vertexs.length; i++) {
            if(vertexs[i] == ch) {
                return i;
            }
        }
        return -1; //找不到返回-1
    }

    // 获取图中的边,放到EData[]数组中
    private EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = i + 1; j < vertexs.length; j++) {
                if(matrix[i][j] != INF) {
                    edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    //获取下标为i的顶点的终点,用于后面判断两个顶点的终点是否相同
    //解决回路问题
    private int getEnd(int[] ends, int i) {
        while(ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

    //算法实现
    public void kruskal() {
        int index = 0; // 表示最后结果数组的索引
        int[] ends = new int[edgeNum]; // 保存已有最小生成树中的每个顶点在最小生成树中的终点
        //创建结果数组
        EData[] rets = new EData[edgeNum];

        //获取图中所有的边的集合
        EData[] edges = getEdges();

        //排序
        sortEdge(edges);

        //遍历edges数组,将边添加到最小生成树时,判断是否有回路
        for (int i = 0; i < edgeNum; i++) {
            int p1 = getPosition(edges[i].start);
            int p2 = getPosition(edges[i].end);
            
            int m = getEnd(ends, p1);
            int n = getEnd(ends, p2);

            //判断是否构成回路
            if(m != n) { //没有构成回路
                ends[m] = n;
                rets[index++] = edges[i];
            }
        }
        
        System.out.println("最小生成树为:");
        for (int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }
    }
}

//此对象实例表示一条边
class EData{
    char start; // 边的起点
    char end; // 边的终点
    int weight; // 边的权值

    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "EData [start=" + start + ", end=" + end + ", weight=" + weight + "]";
    }
}

13.最短路径

13.1迪杰斯特拉算法

package Algorithm;

import java.util.Arrays;

/**
 * 迪杰斯特拉算法:
 * 设置出发顶点为v,顶点集合V{v1, v2, vi...},v到V中各顶点的距离集合Dis,
 * Dis{d1, d2, di...},Dis集合记录着v到图中各顶点的距离(到自身可看作0,v
 * 到vi距离对应为di)
 * (1).从Dis中选择值最小的di并移出Dis集合,同时移出V集合中对应的顶点vi,此时的v到vi即为最短路径
 * (2).更新Dis集合,更新规则为:比较v到V集合中顶点的距离值,与v通过vi到V集合中顶点的距离值,保留值较小的一个(同时也应该更新顶点的前驱节点为vi,表明是通过vi到达的)
 * (3).重复执行两步骤,直到最短路径顶点为目标顶点即可结束
 */
public class DijkstraAlgorithm {
    public static void main(String[] args) {
        char[] vertex = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535; // 表示不可连接
        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, N, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, N, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, N, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
        //创建Graph对象
        Graph graph = new Graph(vertex, matrix);
        //看看图的邻接矩阵
        graph.showGraph();

        graph.dsj(6);
        //展示
        graph.showDijkstra();
    }    
}

class Graph{
    private char[] vertex; //顶点数组
    private int[][] matrix; //邻接数组
    private VisitedVertex vv; // 表示已经访问过的节点的集合
     
    public Graph(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;
    }

    //显示图
    public void showGraph() {
        for (int[] link : matrix) {
            System.out.println(Arrays.toString(link));
        }
    }

    public void dsj(int index) {
        vv = new VisitedVertex(vertex.length, index);
        update(index);
        for (int i = 1; i < vertex.length; i++) {
            index = vv.updateArr(); // 选择并返回新的访问顶点
            update(index);
        }
    }

    /**
     * 更新index下标顶点到周围顶点的距离和周围顶点的前驱顶点
     * @param index
     */
    private void update(int index) {
        int len = 0;
        for (int i = 0; i < matrix[index].length; i++) {
            //从出发顶点到index顶点的距离 + 从index顶点到i顶点的距离的和
            len = vv.getDis(index) + matrix[index][i];
            if(!vv.in(i) && len < vv.getDis(i)) {
                vv.updatePre(i, index); // 更新i顶点的前驱为index顶点
                vv.updateDis(i, len); // 更新出发顶点到i顶点的距离
            }
        }
    }

    //显示迪杰斯特拉
    public void showDijkstra() {
        vv.show();
    }
}

//已访问顶点集合
class VisitedVertex {
    //记录各个顶点是否已经访问过 1表示访问过,0未访问过,会动态更新
    public int[] already_arr;
    //每个下标对应的值为前一个顶点下标,会动态更新
    public int[] pre_visited;
    //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到其他顶点的距离,会动态更新,求的最短距离就会存放到dis
    public int[] dis;

    /**
     * 构造器
     * @param length 表示顶点的个数
     * @param index  表示出发顶点
     */
    public VisitedVertex(int length, int index) {
        this.already_arr = new int[length];
        this.pre_visited = new int[length];
        this.dis = new int[length];
        Arrays.fill(dis, 65535);
        this.dis[index] = 0; // 设置出发顶点的访问距离为0
    }

    /**
     *  判断index顶点是否被访问过
     * @param index
     * @return
     */
    public boolean in(int index) {
        return already_arr[index] == 1;
    }

    /**
     * 更新出发顶点到index顶点的距离
     * @param index
     * @param len
     */
    public void updateDis(int index, int len) {
        dis[index] = len;
    }

    /**
     * 更新pre顶点的前驱顶点为index顶点
     * @param pre
     * @param index
     */
    public void updatePre(int pre, int index) {
        pre_visited[pre] = index;
    }

    /**
     * 返回出发顶点到index顶点的距离
     * @param index
     * @return
     */
    public int getDis(int index) {
        return dis[index];
    }

    //继续选择并返回新的访问顶点
    public int updateArr() {
        int min = 65535, index = 0;
        for (int i = 0; i < already_arr.length; i++) {
            if(already_arr[i] == 0 && dis[i] < min) {
                min = dis[i];
                index = i;
            }
        }
        //更新index顶点被访问过
        already_arr[index] = 1;
        return index;
    }

    //显示最后的结果
    public void show() {
        System.out.println("======================");
        for (int i : already_arr) {
            System.out.print(i + " ");
        }
        System.out.println();
        for (int i : pre_visited) {
            System.out.println(i + " ");
        }
        System.out.println();
        for (int i : dis) {
            System.out.println(i + " ");
        }
    }
}

13.2弗洛伊德算法

package Algorithm;

import java.util.Arrays;

/**
 * 弗洛伊德算法
 * 计算各个顶点之间的最短路径
 */
public class FloydAlgorithm {
    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //创建邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[]{0, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, 0, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};

        FloydGraph graph = new FloydGraph(vertex.length, matrix, vertex);
        graph.show();
    }
}

class FloydGraph{
    private char[] vertex;
    private int[][] dis;
    private int[][] pre;

    /**
     * @param length 大小
     * @param matrix 邻接矩阵
     * @param vertex 顶点数组
     */
    public FloydGraph(int length, int[][] matrix, char[] vertex) {
        this.vertex = vertex;
        this.dis = matrix;
        this.pre = new int[length][length];
        //对pre数组初始化,存放前驱顶点的下标
        for (int i = 0; i < length; i++) {
            Arrays.fill(pre[i], i);
        }
    }

    //显示pre数组和dis数组
    public void show() {
        for (int i = 0; i < dis.length; i++) {
            //输出pre数组
            for (int j = 0; j < dis.length; j++) {
                System.out.println(vertex[pre[i][j]] + " ");
            }
            //输出dis数组
            for (int j = 0; j < dis.length; j++) {
                System.out.println("("+ vertex[i] + "到"+ vertex[j] + "的最短路径是" + dis[i][j] + ")");
            }
        }
    }

    //弗洛伊德算法
    public void floyd() {
        int len = 0; // 变量保存距离
        for (int k = 0; k < dis.length; k++) {
            for (int i = 0; i < dis.length; i++) {
                for (int j = 0; j < dis.length; j++) {
                    len = dis[i][k] + dis[k][j];
                    if(len < dis[i][j]) {
                        dis[i][j] = len; // 更新距离
                        pre[i][j] = pre[k][j]; // 更新前驱顶点
                    }
                }
            }
        }
    }
}

14.贪心算法

package Algorithm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

/**
 * 贪心算法:每一步都选当前最优的情况
 * 利用贪心算法解决广播电台问题
 */
public class GreedyAlgorithm {
    public static void main(String[] args) {
        //创建广播电台
        HashMap<String, HashSet<String>> broadcasts = new HashMap<String, HashSet<String>>();
        //将各个电台放入到broadcasts中
        HashSet<String> hashSet1 = new HashSet<String>();
        hashSet1.add("北京");
        hashSet1.add("上海");
        hashSet1.add("天津");

        HashSet<String> hashSet2 = new HashSet<String>();
        hashSet2.add("广州");
        hashSet2.add("北京");
        hashSet2.add("深圳");

        HashSet<String> hashSet3 = new HashSet<String>();
        hashSet3.add("成都");
        hashSet3.add("上海");
        hashSet3.add("杭州");

        HashSet<String> hashSet4 = new HashSet<String>();
        hashSet4.add("上海");
        hashSet4.add("天津");

        HashSet<String> hashSet5 = new HashSet<String>();
        hashSet5.add("杭州");
        hashSet5.add("大连");

        broadcasts.put("K1", hashSet1);
        broadcasts.put("K2", hashSet2);
        broadcasts.put("K3", hashSet3);
        broadcasts.put("K4", hashSet4);
        broadcasts.put("K5", hashSet5);
        
        //所有的地方
        HashSet<String> allAreas = new HashSet<String>();
        allAreas.add("北京");
        allAreas.add("上海");
        allAreas.add("天津");
        allAreas.add("广州");
        allAreas.add("深圳");
        allAreas.add("成都");
        allAreas.add("杭州");
        allAreas.add("大连");

        //存放选择的电台
        ArrayList<String> selects = new ArrayList<>();

        //临时存放的集合
        HashSet<String> tempSet = new HashSet<>();

        //maxKey能够覆盖最大未覆盖的地区
        String maxKey = null;
        while(allAreas.size() != 0) {
            for (String key : broadcasts.keySet()) {
                tempSet.clear();
                //当前这个key能够覆盖的地区
                HashSet<String> areas = broadcasts.get(key);
                tempSet.addAll(areas);
                //求出tempSet和allAreas的交集
                tempSet.retainAll(allAreas);
                //下边这个体现出贪婪算法的特征
                if(tempSet.size() > 0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())){
                    maxKey = key;
                }
            }
            if(maxKey != null) {
                selects.add(maxKey);
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }

        System.out.println("得到的选择结果是:" + selects);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值