Java数据结构与算法实现

1.数据结构

1.1 线性表

定义接口

public interface List {
    //返回线性表的大小,即数据元素的个数。
    public int getSize();

    //如果线性表为空返回true,否则返回false。
    public boolean isEmpty();

    //判断线性表是否包含数据元素e
    public boolean contains(Object e);

    //返回数据元素e在线性表中的序号
    public int indexOf(Object e);

    //将数据元素e插入到线性表中i号位置
    public void insert(int i, Object e) throws OutOfBoundaryException;

    //将数据元素e插入到元素obj之前
    public boolean insertBefore(Object obj, Object e);

    //将数据元素e插入到元素obj之后
    public boolean insertAfter(Object obj, Object e);

    //删除线性表中序号为i的元素,并返回之
    public Object remove(int i) throws OutOfBoundaryException;

    //删除线性表中第一个与e相同的元素
    public boolean remove(Object e);

    //替换线性表中序号为i的数据元素为e,返回原数据元素
    public Object replace(int i, Object e) throws OutOfBoundaryException;

    //返回线性表中序号为i的数据元素
    public Object get(int i) throws OutOfBoundaryException;
}

1.1.1线性表的顺序存储与实现

public class SequentialList implements List {
    //private final int LEN = 8;  //数组的默认大小
    private int size;          //线性表中数据元素的个数
    private Object[] elements; //数据元素数组

    //构造方法
    public SequentialList(int maxSize){
        size = 0;
        elements = new Object[maxSize];
    }

    //返回线性表的大小,即数据元素的个数。
    public int getSize(){
        return size;
    }

    //如果线性表为空返回true,否则返回false。
    public boolean isEmpty(){
        return size == 0;
    }

    //判断线性表是否包含数据元素e
    public boolean contains(Object e){
        for(int i = 0; i < size; i++){
            if(Objects.equals(e,elements[i]))
                return true;
        }
        return false;
    }

    //返回数据元素e在线性表中的序号
    public int indexOf(Object e){
        for(int i = 0; i < size; i++){
            if(Objects.equals(e,elements[i]))
                return i;
        }
        return -1;
    }

    //将数据元素e插入到线性表中i号位置
    public void insert(int i, Object e) throws OutOfBoundaryException {
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        if(size >= elements.length)
            //expandSpace();
            throw new OutOfBoundaryException("顺序表已满");
        for(int j = size; j > i; j--)
            elements[j] = elements[j-1];
        elements[i] = e;
        size++;
    }
//    private void expandSpace(){
//        Object[] a = new Object[elements.length*2];
//        for(int i = 0; i < elements.length; i++)
//            a[i] = elements[i];
//        elements = a;
//    }

    //将数据元素e插入到元素obj之前
    public boolean insertBefore(Object obj, Object e){
        int i = indexOf(obj);
        if(i < 0)
            return false;
        insert(i,e);
        return true;
    }

    //将数据元素e插入到元素obj之后
    public boolean insertAfter(Object obj, Object e){
        int i = indexOf(obj);
        if(i < 0)
            return false;
        insert(i+1,e);
        return true;
    }

    //删除线性表中序号为i的元素,并返回之
    public Object remove(int i) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        Object obj = elements[i];
        for(int j = i; j < size - 1; j++)
            elements[j] = elements[j+1];
        elements[--size] = null;
        return obj;
    }

    //删除线性表中第一个与e相同的元素
    public boolean remove(Object e){
        int i = indexOf(e);
        if(i < 0)
            return false;
        remove(i);
        return true;
    }

    //替换线性表中序号为i的数据元素为e,返回原数据元素
    public Object replace(int i,Object e) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        Object obj = elements[i];
        elements[i] = e;
        return obj;
    }

    //返回线性表中序号为i的数据元素
    public Object get(int i) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        return elements[i];
    }



    //测试
    public void print(){
        for(int i = 0; i < size; i++){
            System.out.print(get(i)+" ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        SequentialList list = new SequentialList(10);
        list.insert(0,"a");
        list.insert(1,"b");
        list.insert(2,"c");
        list.insert(3,"d");
        list.insert(4,"e");
        list.print();
        list.remove(3);
        list.print();
    }
}

定义异常类

public class OutOfBoundaryException extends RuntimeException{
    public OutOfBoundaryException(String err){
        super(err);
    }
}

1.1.1线性表的链式存储与实现

定义单链表结点

public class SLNode{
    private Object data;
    private SLNode next;

    public SLNode(){
        this(null,null);
    }
    public SLNode(Object data){
        this(data,null);
    }
    public SLNode(Object data, SLNode next){
        this.data = data;
        this.next = next;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public SLNode getNext() {
        return next;
    }

    public void setNext(SLNode next) {
        this.next = next;
    }
}

public class LinkedList implements List {
    private SLNode head;    //单链表头结点引用
    private int size;

    public LinkedList(){
        size = 0;
        head = new SLNode();
    }

    // 辅助方法:获取数据元素e所在结点的前驱结点
    private SLNode getPreNode(Object e){
        SLNode p = head;
        while(p.getNext() != null){
            if(Objects.equals(p.getNext().getData(),e))
                return p;
            else
                p = p.getNext();
        }
        return null;
    }
    // 辅助方法:获取序号为0<=i<size的元素所在结点的前驱结点
    private SLNode getPreNode(int i){
        SLNode p = head;
        for(; i > 0; i--)
            p = p.getNext();
        return p;
    }
    //获取序号为0<=i<size的元素所在结点
    private SLNode getNode(int i){
        SLNode p = head.getNext();
        for(; i > 0; i--)
            p = p.getNext();
        return p;
    }

    //返回线性表的大小,即数据元素的个数。
    public int getSize(){
    return size;
}

    //如果线性表为空返回true,否则返回false。
    public boolean isEmpty(){
        return size == 0;
    }

    //判断线性表是否包含数据元素e
    public boolean contains(Object e){
        SLNode p = head.getNext();
        while(p != null){
            if(Objects.equals(p.getData(),e))
                return true;
            else
                p = p.getNext();
        }
        return false;
    }

    //返回数据元素e在线性表中的序号
    public int indexOf(Object e){
        SLNode p = head.getNext();
        int index = 0;
        while(p != null){
            if(Objects.equals(p.getData(),e))
                return index;
            else{
                index++;
                p = p.getNext();
            }
        }
        return -1;
    }

    //将数据元素e插入到线性表中i号位置
    public void insert(int i, Object e) throws OutOfBoundaryException {
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        SLNode p = getPreNode(i);
        SLNode q = new SLNode(e,p.getNext());
        p.setNext(q);
        size++;
    }

    //将数据元素e插入到元素obj之前
    public boolean insertBefore(Object obj, Object e){
        SLNode p = getPreNode(obj);
        if(p != null){
            SLNode q = new SLNode(e,p.getNext());
            p.setNext(q);
            size++;
            return true;
        }
        return false;
    }

    //将数据元素e插入到元素obj之后
    public boolean insertAfter(Object obj, Object e){
        SLNode p = head.getNext();
        while(p != null){
            if(Objects.equals(p.getData(),obj)){
                SLNode q = new SLNode(e,p.getNext());
                p.setNext(q);
                size++;
                return true;
            }
            else p = p.getNext();
        }
        return false;
    }

    //默认尾部添加元素
    public void add(Object obj) { insert(getSize(),obj);}

    //删除线性表中序号为i的元素,并返回之
    public Object remove(int i) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        SLNode p = getPreNode(i);
        Object obj = p.getNext().getData();
        p.setNext(p.getNext().getNext());
        size--;
        return obj;
    }

    //删除线性表中第一个与e相同的元素
    public boolean remove(Object e){
        SLNode p = getPreNode(e);
        if(p != null){
            p.setNext(p.getNext().getNext());
            size--;
            return true;
        }
        return false;
    }

    //替换线性表中序号为i的数据元素为e,返回原数据元素
    public Object replace(int i,Object e) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        SLNode p = getNode(i);
        Object obj = p.getData();
        p.setData(e);
        return obj;
    }

    //返回线性表中序号为i的数据元素
    public Object get(int i) throws OutOfBoundaryException{
        if(i < 0 || i > size)
            throw new OutOfBoundaryException("错误,指定的插入序号越界。");
        SLNode p = getNode(i);
        return p.getData();
    }

    //测试
    //打印输出所有结点
    public void print() {
        SLNode node = head.getNext();
        while (node != null) {
            System.out.print(node.getData() + " ");
            node = node.getNext();
        }
        System.out.println();
    }
    public static void main(String[] args) {
        LinkedList list = new LinkedList();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        list.add("eee");
        list.print();
        System.out.println(list.indexOf("ccc"));
        System.out.println(list.get(3));
        System.out.println(list.remove(2));
        list.print();
        list.insert(1,"abc");
        list.print();
        list.replace(1,"abcd");
        list.print();
    }
}

1.2 栈

定义接口

public interface Stack {
    //返回堆栈的大小
    public int getSize();
    //判断堆栈是否为空
    public boolean isEmpty();
    //数据元素e入栈
    public void push(Object e);
    //栈顶元素出栈
    public Object pop() throws StackEmptyException;
    //取栈顶元素,但不出栈
    public Object peek() throws StackEmptyException;
}

1.2.1 栈的顺序存储实现

public class SequentialStack implements Stack {
    private final int LEN = 8;  //数组的默认大小
    private Object[] elements;  //数据元素数组
    private int top;            //栈顶指针

    public SequentialStack(){
        top = -1;   //top=-1表示空栈
        elements = new Object[LEN];
    }

    //返回堆栈的大小
    public int getSize(){
        return top + 1;
    }

    //判断堆栈是否为空
    public boolean isEmpty(){
        return top < 0;
    }

    //数据元素e入栈
    public void push(Object e){
        if(getSize() >= elements.length)
            expandSpace();
        elements[++top] = e;
    }
    //数组扩容
    private void expandSpace(){
        Object[] a = new Object[elements.length*2];
        for(int i = 0; i < elements.length; i++)
            a[i] = elements[i];
        elements = a;
    }

    //栈顶元素出栈
    public Object pop() throws StackEmptyException{
        if(getSize() < 1)
            throw new StackEmptyException("错误,堆栈为空");
        Object obj = elements[top];
        elements[top--] = null;
        return obj;
    }

    //取栈顶元素,但不出栈
    public Object peek() throws StackEmptyException{
        if(getSize() < 1)
            throw new StackEmptyException("错误,堆栈为空");
        return elements[top];
    }

    //测试
    public void print(){
        for(int i = top; i >= 0; i--){
            System.out.print(elements[i]+" ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        SequentialStack stack = new SequentialStack();
        stack.push("aaa");
        stack.push("bbb");
        stack.push("ccc");
        stack.push("ddd");
        stack.push("eee");

        stack.print();
        System.out.println(stack.peek());
        System.out.println(stack.pop());
        System.out.println(stack.peek());
        stack.print();
        System.out.println(stack.getSize());
    }
}

1.2.2 栈的链式存储实现

public class LinkedStack implements Stack {
    private SLNode top;    //链表首结点引用
    private int size;
    public LinkedStack(){
        top = null;
        size = 0;
    }

    //返回堆栈的大小
    public int getSize(){
        return size;
    }

    //判断堆栈是否为空
    public boolean isEmpty(){
        return size == 0;
    }

    //数据元素e入栈
    public void push(Object e){
        SLNode q = new SLNode(e,top);
        top = q;
        size++;
    }

    //栈顶元素出栈
    public Object pop() throws StackEmptyException {
        if(size < 1)
            throw new StackEmptyException("错误,堆栈为空");
        Object obj = top.getData();
        top = top.getNext();
        size--;
        return obj;
    }

    //取栈顶元素,但不出栈
    public Object peek() throws StackEmptyException{
        if(size < 1)
            throw new StackEmptyException("错误,堆栈为空");
        return top.getData();
    }


    //测试
    public void print(){
        SLNode p = top;
        while(p != null){
            System.out.print(p.getData() + " ");
            p = p.getNext();
        }
        System.out.println();
    }

    public static void main(String[] args) {
        LinkedStack stack = new LinkedStack();
        stack.push("aaa");
        stack.push("bbb");
        stack.push("ccc");
        stack.push("ddd");
        stack.push("eee");

        stack.print();
        System.out.println(stack.peek());
        System.out.println(stack.pop());
        System.out.println(stack.peek());
        stack.print();
        System.out.println(stack.getSize());
    }
}

1.3 队列

定义接口

public interface Queue {
    //返回队列的大小
    public int getSize();
    //判断队列是否为空
    public boolean isEmpty();
    //数据元素e入队
    public void enqueue(Object e);
    //队首元素出队
    public Object dequeue() throws QueueException;
    //取队首元素,但不出队
    public Object peek() throws QueueException;
}

1.3.1 队列的顺序存储实现

如果队列当作一般的表用数组加以实现,则在删除元素时需要将其余元素都向前移动一个位置,需要O(n)时间,为提高效率,逻辑上采用循环数组实现循环队列。
入队时 队尾指针rear逆时针移动一位
出队时 队首指着front逆时针移动一位
解决区分满队列和空队列的方式有两种
①少使用一个存储空间,当队尾指针的下一单元就是队首指针所指单元时,则停止入队。(本次采用此方案)
②增设size标志 size=0则队空 size=capacity则队满

public class SequentialQueue implements Queue {
    private Object[] elements;
    private int capacity;   //数组的大小
    private int front;      //队首指针,指向队首
    private int rear;       //队尾指针,指向队尾后一个位置

    public SequentialQueue(){
        this(10);
    }
    public SequentialQueue(int cap){
        this.capacity = cap + 1;
        elements = new Object[capacity];
        front = rear = 0;
    }

    //返回队列的大小
    public int getSize(){
        return (rear - front + capacity) % capacity;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return front == rear;
    }

    //数据元素e入队
    public void enqueue(Object e) throws QueueException{
        if(getSize() == capacity - 1)
            throw new QueueException("错误:队列已满");
        elements[rear] = e;
        rear = (rear + 1) % capacity;
    }

    //队首元素出队
    public Object dequeue() throws QueueException{
        if(isEmpty())
            throw new QueueException("错误:队列为空");
        Object obj = elements[front];
        elements[front] = null;
        front = (front + 1) % capacity;
        return obj;
    }

    //取队首元素,但不出队
    public Object peek() throws QueueException{
        if(isEmpty())
            throw new QueueException("错误:队列为空");
        return elements[front];
    }


    //测试
    public static void main(String[] args) {
        SequentialQueue queue = new SequentialQueue(6) ;
        queue.enqueue("A");
        queue.enqueue("B");
        queue.enqueue("C");
        queue.enqueue("D");
        queue.enqueue("E");
        queue.enqueue("F");

        System.out.println(queue.getSize());
        System.out.println(queue.peek());
        System.out.println(queue.dequeue());
        System.out.println(queue.peek());

    }
}

定义异常类

public class QueueException extends RuntimeException{
    public QueueException(String err){
        super(err);
    }
}

1.3.2 队列的链式存储实现

public class LinkedQueue implements Queue{
    private SLNode front;
    private SLNode rear;
    private int size;
    public LinkedQueue(){
        front = new SLNode();
        rear = front;
        size = 0;
    }

    //返回队列的大小
    public int getSize(){
        return size;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return size == 0;
    }

    //数据元素e入队
    public void enqueue(Object e){
        SLNode p = new SLNode(e);
        rear.setNext(p);
        rear = p;
        size++;
    }

    //队首元素出队
    public Object dequeue() throws QueueException{
        if(size < 1)
            throw new QueueException("错误:队列为空");
        SLNode p = front.getNext();
        front.setNext(p.getNext());
        size--;
        if(size < 1)
            rear = front;    //如果队列为空,rear指向头结点
        return p.getData();
    }

    //取队首元素,但不出队
    public Object peek() throws QueueException{
        if(size < 1)
            throw new QueueException("错误:队列为空");
        return front.getNext().getData();
    }


    //测试
    public static void main(String[] args) {
        SequentialQueue queue = new SequentialQueue() ;
        queue.enqueue("A");
        queue.enqueue("B");
        queue.enqueue("C");
        queue.enqueue("D");
        queue.enqueue("E");
        queue.enqueue("F");

        System.out.println(queue.getSize());
        System.out.println(queue.peek());
        System.out.println(queue.dequeue());
        System.out.println(queue.dequeue());
        System.out.println(queue.peek());
    }
}

1.4 树

定义二叉树的节点类

public class BinTreeNode {
    private Object data;        //数据域
    private BinTreeNode lChild; //左孩子
    private BinTreeNode rChild; //右孩子

    public BinTreeNode() {this(null); }

    public BinTreeNode(Object data) {
        this.data = data;
    }

    public BinTreeNode(Object data, BinTreeNode lChild, BinTreeNode rChild) {
        this.data = data;
        this.lChild = lChild;
        this.rChild = rChild;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public BinTreeNode getlChild() {
        return lChild;
    }

    public void setlChild(BinTreeNode lChild) {
        this.lChild = lChild;
    }

    public BinTreeNode getrChild() {
        return rChild;
    }

    public void setrChild(BinTreeNode rChild) {
        this.rChild = rChild;
    }
}
public class BinTree {
    private BinTreeNode root;    //根节点

    public BinTree() { root = null; }
    public BinTree(BinTreeNode root) { this.root = root; }

    public BinTreeNode getRoot() { return root; }
    public void setRoot(BinTreeNode root) { this.root = root; }

    /**
     *  由先根遍历序列和中根遍历序列 创建一个二叉树
     *  先序遍历序列中,第一个结点必为根节点;中序遍历序列被根节点分为两部分:根结点之前的部分为左子树结点中序序列,根结点之后的为右子树结点中序序列
     *  设中序序列根节点之前的部分的长度i,则先序序列根节点之后的i个结点属于左子树(第一个为左子树根结点)
     *  preIndex表示先序序列开始位置,inIndex表示中序序列开始位置,count表示序列长度
     */
    public BinTree(String preOrder, String inOrder, int preIndex, int inIndex, int count){
        if (count > 0) {
            char r = preOrder.charAt(preIndex);			//取先根遍历序列中的第一个结点作为根结点
            int i = 0;
            for (; i < count; i++) {
                if (r == inOrder.charAt(i + inIndex)) { //寻找根结点在中根遍历序列中的位置
                    break;
                }
            }
            root = new BinTreeNode(r);
            root.setlChild(new BinTree(preOrder, inOrder, preIndex+1, inIndex, i).root);
            root.setrChild(new BinTree(preOrder, inOrder, preIndex+i+1, inIndex+i+1, count-i-1).root);
        }
    }
    //先序遍历二叉树
    public void preOrder(){
        preOrderRecursion(this.root);
        System.out.println();
        preOrderTraverse(this.root);
        System.out.println();
        preOrderTraverse1(this.root);
        System.out.println();
    }
    //先序遍历的递归算法
    private void preOrderRecursion(BinTreeNode rt){
        if(rt == null) return;  //递归基,空树直接返回
        System.out.print(rt.getData());  //访问根结点
        preOrderRecursion(rt.getlChild()); //遍历左子树
        preOrderRecursion(rt.getrChild()); //遍历右子树
    }
    /**先序遍历的非递归算法1
     *1.沿着根节点p一直向左走,沿途访问经过的根节点,并将这些根节点的非空右子树入栈,直到p为空
     *2.取出栈顶结点,在外层循环中继续先序遍历这棵以p指向的子树。
     *3.如果堆栈为空,则表示再没有右子树需要遍历,此时结束外层循环,完成整棵树的先序遍历
     */
    private void preOrderTraverse(BinTreeNode rt){
        if(rt == null) return;
        BinTreeNode p = rt;
        Stack s = new LinkedStack();
        while(p != null){
            while(p != null){    //向左走到尽头
                System.out.print(p.getData()); //访问根
                if(p.getrChild() != null)
                    s.push(p.getrChild());  //右子树根节点入栈
                p = p.getlChild();
            }
            if(!s.isEmpty())
                p = (BinTreeNode)s.pop();   //右子树根退栈遍历右子树
        }
    }
    /**先序遍历的非递归算法2
     *1.沿着根节点p一直向左走,沿途访问根节点,将根节点入栈,直到p为空
     *2.取出上一层根节点,然后转向该根节点的右子树进行先序遍历
     *3.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
     */
    private void preOrderTraverse1(BinTreeNode rt){
        if(rt == null) return;
        BinTreeNode p = rt;
        Stack s = new LinkedStack();
        while(p != null || !s.isEmpty()){
            while(p != null){    //向左走到尽头
                System.out.print(p.getData()); //访问根
                s.push(p);  //根节点入栈
                p = p.getlChild();
            }
            p = (BinTreeNode)s.pop();   //根退栈遍历右子树
            p = p.getrChild();
        }
    }
    //中序遍历二叉树
    public void inOrder(){
        inOrderRecursion(this.root);
        System.out.println();
        inOrderTraverse(this.root);
        System.out.println();
    }
    //中序遍历的递归算法
    private void inOrderRecursion(BinTreeNode rt){
        if(rt == null) return;  //递归基,空树直接返回
        inOrderRecursion(rt.getlChild()); //遍历左子树
        System.out.print(rt.getData());  //访问根结点
        inOrderRecursion(rt.getrChild()); //遍历右子树
    }

    /**中序遍历的非递归算法
     *1.沿着根节点p一直向左走,沿途将根节点入栈,直到p为空
     *2.取出上一层根节点访问之,然后转向该根节点的右子树进行中序遍历
     *3.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
     */
    private void inOrderTraverse(BinTreeNode rt) {
        if (rt == null) return;
        BinTreeNode p = rt;
        Stack s = new LinkedStack();

        while(p != null || !s.isEmpty()){
            while(p != null){       //一直往左走
                s.push(p);          //将根节点入栈
                p = p.getlChild();
            }
            p = (BinTreeNode)s.pop();  //取出栈顶根节点访问之
            System.out.print(p.getData());
            p = p.getrChild();      //转向根的右子树进行遍历
        }
    }

    //后序遍历二叉树
    public void postOrder(){
        postOrderRecursion(this.root);
        System.out.println();
        postOrderTraverse(this.root);
        System.out.println();
    }
    //后序遍历的递归算法
    private void postOrderRecursion(BinTreeNode rt){
        if(rt == null) return;  //递归基,空树直接返回
        postOrderRecursion(rt.getlChild()); //遍历左子树
        postOrderRecursion(rt.getrChild()); //遍历右子树
        System.out.print(rt.getData());  //访问根结点
    }
    /**后序遍历的非递归算法
     *1.第一个while循环中,沿着根节点p先向左子树深入,如果左子树为空,则向右子树深入,沿途将根节点入栈,直到p为空
     *2.第一个if语句说明应该取出栈顶根节点访问,此时栈顶结点为叶子或无右子树的单分支结点
     *3.访问p之后,说明以p为根的子树访问完毕,判断p是否为其父节点的右孩子(当前栈顶即为其父节点),如果是,
     * 则说明只要访问其父亲就可以完成对以p的父亲为根的子树的遍历;如果不是,则转向其父节点的右子树继续后序遍历
     *4.如果堆栈和p都为空,则表示没有更多的子树需要遍历,此时结束外层循环,完成整棵树的遍历
     */
    private void postOrderTraverse(BinTreeNode rt) {
        if (rt == null) return;
        BinTreeNode p = rt;
        Stack s = new LinkedStack();

        while (p != null || !s.isEmpty()) {
            while(p != null){  //先左后右不断深入
                s.push(p);     //将根节点入栈
                if(p.getlChild() != null)
                    p = p.getlChild();
                else
                    p = p.getrChild();
            }
            if(!s.isEmpty()){
                p = (BinTreeNode)s.pop();  //取出栈顶根节点访问之
                System.out.print(p.getData());
            }
            //满足条件时,说明栈顶根节点右子树已访问,应出栈访问之
            while(!s.isEmpty() && ((BinTreeNode)s.peek()).getrChild() == p){
                p = (BinTreeNode)s.pop();
                System.out.print(p.getData());
            }
            //转向栈顶根节点的右子树继续后序遍历
            if(!s.isEmpty())
                p = ((BinTreeNode)s.peek()).getrChild();
            else
                p = null;
        }
    }
    //层次遍历二叉树
    public void levelOrder(){
        levelOrderTraverse(this.root);
        System.out.println();
    }
    //使用队列完成二叉树的按层遍历
    private void levelOrderTraverse(BinTreeNode rt) {
        if(rt == null) return;
        Queue q = new SequentialQueue();
        q.enqueue(rt);         //根节点入队
        while(!q.isEmpty()){
            BinTreeNode p = (BinTreeNode)q.dequeue(); //取出队首结点p并访问
            System.out.print(p.getData());
            if(p.getlChild() != null)   //将p的非空左右孩子依次入队
                q.enqueue(p.getlChild());
            if(p.getrChild() != null)
                q.enqueue(p.getrChild());
        }
    }
    //在树中查找元素e,返回其所在结点
    public BinTreeNode find(Object e){
        return searchE(root,e);
    }
    //递归查找元素e
    private BinTreeNode searchE(BinTreeNode rt,Object e){
        if(rt == null) return null;
        if(Objects.equals(rt.getData(),e)) return rt; //如果是根节点,返回根
        BinTreeNode v = searchE(rt.getlChild(),e);   //否则在左子树中找
        if(v == null) v = searchE(rt.getrChild(),e); //没找到,在右子树中找
        return v;
    }
    //统计结点数,基于先根遍历
    public int countNode(BinTreeNode rt){
        if(rt != null)
            return countNode(rt.getlChild())+countNode(rt.getrChild())+1;
        else
            return 0;
    }
    /**
     * 二叉树的深度
     * 基于后根遍历
     * 先求出左子树的深度,在求出右子树的深度,二者较大的一方+1就是二叉树深度
     */
    public int getDepth(BinTreeNode rt) {
        if (rt != null) {
            int lDepth = getDepth(rt.getlChild());				//遍历左子树
            int rDepth = getDepth(rt.getrChild());				//遍历右子树
            return (lDepth > rDepth ? lDepth : rDepth) + 1;     //访问根结点
        }
        return 0;
    }

    //测试
    public static void main(String[] args) {
        /*----------------------
					 A
				B		   C
			 D     E    F
				 G		  H
		 -----------------------*/
        //创建一个二叉树(由叶子结点往上依次添加,最后是根结点)
        BinTreeNode d = new BinTreeNode('D');
        BinTreeNode g = new BinTreeNode('G');
        BinTreeNode h = new BinTreeNode('H');
        BinTreeNode e = new BinTreeNode('E', g, null);
        BinTreeNode b = new BinTreeNode('B', d, e);
        BinTreeNode f = new BinTreeNode('F', null, h);
        BinTreeNode c = new BinTreeNode('C', f, null);
        BinTreeNode a = new BinTreeNode('A', b, c);

        BinTree tree = new BinTree(a);
        BinTreeNode root = tree.getRoot();

        System.out.print("先根遍历:");
        System.out.println();
        tree.preOrder();
        System.out.print("中根遍历:");
        System.out.println();
        tree.inOrder();
        System.out.print("后根遍历:");
        System.out.println();
        tree.postOrder();
        System.out.print("层次遍历:");
        System.out.println();
        tree.levelOrder();

        System.out.println(tree.countNode(root));
        System.out.println(tree.getDepth(root));

        String preOrder = "ABDEGCFH";
        String inOrder = "DBGEAFHC";
        BinTree rt = new BinTree(preOrder,inOrder,0,0,preOrder.length());
        System.out.print("后根遍历:");
        System.out.println();
        rt.postOrder();
    }
}

1.5 图

1.5.1 邻接矩阵存储结构

enum GraphKind {
    UDG,	//无向图
    DG,		//有向图
}
//图的顶点类
class Vertex {
    public char lable;
    public boolean wasvisited;
    public Vertex(char lab){
        lable = lab;
        wasvisited = false;
    }
}

public class AGraph {
    private GraphKind kind;		      //图的种类标志
    private final int MAX_VERTS = 20; //顶点最大个数
    private Vertex[] vertexList;      //顶点数组
    private int adjMat[][];           //邻接矩阵
    public int nVerts;                //顶点数

    public AGraph(){
        this(GraphKind.UDG);
    }
    public AGraph(GraphKind kind) {
        this.kind = kind;
        vertexList = new Vertex[MAX_VERTS];
        adjMat = new int[MAX_VERTS][MAX_VERTS];
        nVerts = 0;
        for (int i = 0; i < MAX_VERTS; i++) {
            for (int j = 0; j < MAX_VERTS; j++) {
                adjMat[i][j] = 0;
            }
        }
    }
    //加入顶点
    public void addVertex(char lab){
        vertexList[nVerts ++] = new Vertex(lab);
    }
    //加入边
    public void addEdage(int start,int end){
        switch (kind) {
            case UDG:
                adjMat[start][end] = 1;
                adjMat[end][start] = 1;
                break;
            case DG:
                adjMat[start][end] = 1;
                break;
        }
    }

    //取得未被访问过的邻接点
    public int getAdjUnvisitedVertex(int v){
        for(int i = 0; i < nVerts; i++){
            if (adjMat[v][i] == 1 && vertexList[i].wasvisited == false){
                return i;
            }
        }
        return -1;
    }

    //重置顶点状态
    public void resetVexStatus(){
        for (int i = 0; i < nVerts; i++){
            vertexList[i].wasvisited = false;
        }
    }

    /**
     * 深度优先遍历算法(非递归实现)
     * 从图的某一点x出发,访问任意一个与之相邻且未被访问到的点y,然后访问任意一个与y相邻且未被访问到的点z,…直到所有点都被访问过为止。
     * 1.构造一个栈存放顶点,访问第一个顶点v1,并将v1入栈
     * 2.将v1的未被访问的邻接点入栈并访问,若没有未曾访问的邻接点则出栈
     * 3.重复2直到栈空
     */
    public void DFS(int n){
        Stack s = new LinkedStack();
        vertexList[n].wasvisited = true;
        System.out.print(vertexList[n].lable+" ");
        s.push(n);
        while (!s.isEmpty()){
            int v = getAdjUnvisitedVertex((int)s.peek());  //找到栈当前顶点邻接且未被访问的顶点
            if(v == -1){                     //如果当前顶点值为-1,则表示没有邻接且未被访问顶点,顶点出栈
                s.pop();
            } else {                         //否则访问下一个邻接顶点
                vertexList[v].wasvisited = true;
                System.out.print(vertexList[v].lable+" ");
                s.push(v);
            }
        }
    }
    public void DFSTraverse(){
        for (int i = 0; i < nVerts; i++){
            if(vertexList[i].wasvisited == false){      //确保有向图时每个顶点都被访问到
                DFS(i);
            }
        }
    }
    /**
     * 广度优先遍历算法(非递归实现)
     * 从图的某一点x出发,访问所有与之相邻且未被访问到的点y,z,l,m...,然后分别从这些邻接点出发依次访问它们的所有邻接点,直到所有点都被访问过为止。
     * 1.构造一个队列存放顶点,访问第一个顶点v1,并将v1入队
     * 2.v1出队,将v1的所有未被访问的邻接点入队并访问
     * 3.重复2直到队空
     */
    public void BFS(int n){
        Queue q = new SequentialQueue();
        vertexList[n].wasvisited = true;
        System.out.print(vertexList[n].lable+" ");
        q.enqueue(n);
        int v2;
        while (!q.isEmpty()){
            int v1 = (int)q.dequeue();
            while((v2 = getAdjUnvisitedVertex(v1)) != -1) {
                vertexList[v2].wasvisited = true;
                System.out.print(vertexList[v2].lable+" ");
                q.enqueue(v2);
            }
        }
    }
    public void BFSTraverse(){
        for (int i = 0; i < nVerts; i++){
            if(vertexList[i].wasvisited == false){      //确保有向图时每个顶点都被访问到
                BFS(i);
            }
        }
    }

    //测试
    public static void main(String[] args) {
        AGraph aGraph = new AGraph(GraphKind.UDG);
        aGraph.addVertex('a');
        aGraph.addVertex('b');
        aGraph.addVertex('c');
        aGraph.addVertex('d');
        aGraph.addVertex('e');
        aGraph.addVertex('f');
        aGraph.addEdage(0,1);
        aGraph.addEdage(0,3);
        aGraph.addEdage(0,4);
        aGraph.addEdage(1,2);
        aGraph.addEdage(1,4);
        aGraph.addEdage(2,4);
        aGraph.addEdage(2,5);
        aGraph.addEdage(4,5);
        System.out.println("\n无向图深度优先:");
        aGraph.DFSTraverse();
        aGraph.resetVexStatus();
        System.out.println("\n无向图广度优先:");
        aGraph.BFSTraverse();

        AGraph aGraph1 = new AGraph(GraphKind.DG);
        aGraph1.addVertex('a');
        aGraph1.addVertex('b');
        aGraph1.addVertex('c');
        aGraph1.addVertex('d');
        aGraph1.addVertex('e');
        aGraph1.addVertex('f');
        aGraph1.addEdage(0,1);
        aGraph1.addEdage(0,4);
        aGraph1.addEdage(3,0);
        aGraph1.addEdage(2,1);
        aGraph1.addEdage(1,4);
        aGraph1.addEdage(4,2);
        aGraph1.addEdage(5,2);
        aGraph1.addEdage(4,5);
        System.out.println("\n有向图深度优先:");
        aGraph1.DFSTraverse();
        aGraph1.resetVexStatus();
        System.out.println("\n有向图广度优先:");
        aGraph1.BFSTraverse();
    }
}

1.5.2 邻接表存储结构

enum mGraphKind {
    UDG,	//无向图
    DG,		//有向图
}
public class MGraph<E> {
    // 邻接表中表对应的链表的顶点
    private class ENode<E>{
        private int adjvex;         // 邻接顶点序号
        private int weight;         //弧的权值
        private ENode nextadj;      //下一个邻接表结点

        ENode(int adjvex, int weight){
            this.adjvex = adjvex;
            this.weight = weight;
        }
    }
    // 邻接表中表的顶点
    private class VNode<E>{
        private E name;        //顶点名称
        private ENode firstadj;      //链接表的第一个结点
    }

    private mGraphKind kind;		      //图的种类标志
    private VNode<E>[] vexs;  // 顶点数组
    private final int MAX_VERTS = 20; //顶点最大个数
    private int numOfVexs;    // 顶点的实际数量
    private boolean[] visited;// 判断顶点是否被访问过

    public MGraph(){
        this(mGraphKind.UDG);
    }
    MGraph(mGraphKind kind){
        this.kind = kind;
        vexs = (VNode<E>[]) Array.newInstance(VNode.class, MAX_VERTS);
    }

    //添加顶点
    public void insertVertex(E vertexName){
        if (numOfVexs >= MAX_VERTS)
            System.out.println("超出最大个数");
        VNode<E> vex = new VNode<E>();
        vex.name = vertexName;
        vexs[numOfVexs++] = vex;
    }
    //添加边
    public void insertEdge(int v1, int v2, int weight){
        if (v1 < 0 || v2 < 0 || v1 >= numOfVexs || v2 >= numOfVexs){
            throw new ArrayIndexOutOfBoundsException();
        }
        ENode vex1 = new ENode(v2, weight);
        // 索引为index1的顶点没有邻接顶点
        if (vexs[v1].firstadj == null) {
            vexs[v1].firstadj = vex1;
        }
        // 索引为index1的顶点有邻接顶点
        else {        //链表插入操作
            vex1.nextadj = vexs[v1].firstadj;
            vexs[v1].firstadj = vex1;
        }
        switch (kind) {
            case UDG:
                ENode vex2 = new ENode(v1, weight);
                // 索引为index2的顶点没有邻接顶点
                if (vexs[v2].firstadj == null) {
                    vexs[v2].firstadj = vex2;
                }
                // 索引为index2的顶点有邻接顶点
                else {
                    vex2.nextadj = vexs[v2].firstadj;
                    vexs[v2].firstadj = vex2;
                }
                break;
            case DG:
                break;
        }

    }
    //重置顶点状态
    public void resetVexStatus(){
        for (int i = 0; i < numOfVexs; i++){
            visited[i] = false;
        }
    }

    /**
     * 深度优先遍历算法(非递归实现)
     * 从图的某一点x出发,访问任意一个与之相邻且未被访问到的点y,然后访问任意一个与y相邻且未被访问到的点z,…直到所有点都被访问过为止。
     * 1.构造一个栈存放顶点,访问第一个顶点v1,并将v1入栈
     * 2.v1出栈,将v1的所有未被访问的邻接点入栈并访问
     * 3.重复2直到栈空
     */
    public void depthFirstSearch(int v) {
        if (v < 0 || v >= numOfVexs)
            throw new ArrayIndexOutOfBoundsException();
        Stack stack = new LinkedStack();
        stack.push(v);
        visited[v] = true;
        ENode current;
        while (!stack.isEmpty()) {
            v = (int)stack.pop();
            System.out.print(vexs[v].name + " ");
            current = vexs[v].firstadj;
            while (current != null) {
                if (!visited[current.adjvex]) {
                    stack.push(current.adjvex);
                    visited[current.adjvex] = true;
                }
                current = current.nextadj;
            }
        }
    }

    public void DFSTraverse(){
        visited = new boolean[numOfVexs];
        for (int i = 0; i < numOfVexs; i++){
            if(visited[i] == false){      //确保有向图时每个顶点都被访问到
                depthFirstSearch(i);
            }
        }
    }

    /**
     * 广度优先遍历算法(非递归实现)
     * 从图的某一点x出发,访问所有与之相邻且未被访问到的点y,z,l,m...,然后分别从这些邻接点出发依次访问它们的所有邻接点,直到所有点都被访问过为止。
     * 1.构造一个队列存放顶点,访问第一个顶点v1,并将v1入队
     * 2.v1出队,将v1的所有未被访问的邻接点入队并访问
     * 3.重复2直到队空
     */
    public void breadFirstSearch(int v) {
        if (v < 0 || v >= numOfVexs)
            throw new ArrayIndexOutOfBoundsException();
        Queue queue = new SequentialQueue();
        queue.enqueue(v);
        visited[v] = true;
        ENode current;
        while (!queue.isEmpty()) {
            v = (int)queue.dequeue();
            System.out.print(vexs[v].name + " ");
            current = vexs[v].firstadj;
            while (current != null) {
                if (!visited[current.adjvex]) {
                    queue.enqueue(current.adjvex);
                    visited[current.adjvex] = true;
                }
                current = current.nextadj;
            }
        }
    }

    public void BFSTraverse(){
        visited = new boolean[numOfVexs];
        for (int i = 0; i < numOfVexs; i++){
            if(visited[i] == false){      //确保有向图时每个顶点都被访问到
                breadFirstSearch(i);
            }
        }
    }

    //测试
    public static void main(String[] args) {
        MGraph mGraph = new MGraph(mGraphKind.DG);
        mGraph.insertVertex("V0");
        mGraph.insertVertex("V1");
        mGraph.insertVertex("V2");
        mGraph.insertVertex("V3");
        mGraph.insertVertex("V4");
        mGraph.insertEdge(0,4,6);
        mGraph.insertEdge(1,0,9);
        mGraph.insertEdge(1,2,3);
        mGraph.insertEdge(2,0,2);
        mGraph.insertEdge(2,3,5);
        mGraph.insertEdge(3,4,1);
        System.out.println("\n有向图深度优先:");
        mGraph.DFSTraverse();
        mGraph.resetVexStatus();
        System.out.println("\n有向图广度优先:");
        mGraph.BFSTraverse();

        MGraph mGraph1 = new MGraph(mGraphKind.UDG);
        mGraph1.insertVertex("V0");
        mGraph1.insertVertex("V1");
        mGraph1.insertVertex("V2");
        mGraph1.insertVertex("V3");
        mGraph1.insertVertex("V4");
        mGraph1.insertEdge(0,4,6);
        mGraph1.insertEdge(1,0,9);
        mGraph1.insertEdge(1,2,3);
        mGraph1.insertEdge(2,0,2);
        mGraph1.insertEdge(2,3,5);
        mGraph1.insertEdge(3,4,1);
        System.out.println("\n无向图深度优先:");
        mGraph1.DFSTraverse();
        mGraph1.resetVexStatus();
        System.out.println("\n无向图广度优先:");
        mGraph1.BFSTraverse();
    }
}

2.算法

2.1 查找

2.1.1 顺序查找与二分法查找

public class LinearSearch {
    /**
     * 顺序查找
     * 时间复杂度 最坏:O(n) 最好:O(1) 平均:O(n)
     * 空间复杂度 O(1)
     */
    public static int seqSearch(int[] arr, int key) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == key) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 二分法查找(折半查找)
     * 条件:顺序存储的有序表(从小到大)
     * 时间复杂度 最坏:O(log n) 最好:O(1) 平均:O(log n)
     * 空间复杂度 O(1)
     */
    public static int binSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        while (low <= high) {
            int mid = (low + high) / 2;	//中间位置,当前比较的数据元素位置
            if (arr[mid] == key) {
                return mid;
            } else if (arr[mid] > key) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        int[] arr = {7,5,2,3,4,9,1,0,2};
        System.out.println(LinearSearch.seqSearch(arr, 9));

        int[] brr = {1,2,3,4,5,6,7,8,9};
        System.out.println(LinearSearch.binSearch(brr, 7));
    }
}

2.1.2 哈希表

定义结点

public class Node<K,V> {
    int hash;
    K key;
    V value;
    Node next;
}

实现哈希表:数组+链表,哈希函数暂时采用最简单的取余法

public class MyHashMap<K,V> {

    Node[] table;
    int size;

    public MyHashMap() {
        table = new Node[16];
    }

    public int size() {
        return this.size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public V get(K key) {
        int hash = myHash(key.hashCode(),table.length);
        V value = null;

        if (table[hash] != null) {
            Node temp = table[hash];
            while (temp != null) {
                if (temp.key.equals(key)) {
                    value = (V)temp.value;
                    break;
                } else {
                    temp = temp.next;
                }
            }
        }
        return value;
    }

    public void put(K key, V value) {
        //todo:数组扩容
        Node newNode = new Node();
        newNode.hash = myHash(key.hashCode(),table.length);
        newNode.key = key;
        newNode.value = value;

        Node temp = table[newNode.hash];
        boolean keyRepeat = false;
        Node lastNode = null;

        //此处数组元素为空,则直接将节点放入
        if (temp == null) {
            table[newNode.hash] = newNode;
            size ++;
        } else {
            while (temp != null) {
                //key重复则覆盖
                if (temp.key.equals(key)) {
                    temp.value = value;
                    keyRepeat = true;
                    break;
                } else {//不重复则遍历下一个
                    lastNode = temp;
                    temp = temp.next;
                }
            }
            if(!keyRepeat){ //如果没有重复,则添加至链表最后
                lastNode.next = newNode;
                size ++;
            }
        }
    }
    public int myHash(int v, int length){
        return v & (length - 1);
    }

    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer("{");
        //遍历数组
        for (int i = 0; i < table.length; i++) {
            Node temp = table[i];
            while (temp != null) { //遍历链表
                stringBuffer.append(temp.key + ":" + temp.value + ",");
                temp = temp.next;
            }
        }
        stringBuffer.setCharAt(stringBuffer.length()-1,'}');
        return stringBuffer.toString();
    }

    /**
     * 测试
     */
    public static void main(String[] args){
        MyHashMap<Integer,String> m1 = new MyHashMap<>();
        m1.put(10,"aa");
        m1.put(20,"bb");
        m1.put(30,"cc");
        m1.put(20,"ssss");
        m1.put(26,"aa1");
        m1.put(42,"aa2");

        System.out.println(m1);
        System.out.println(m1.get(30));
        System.out.println(m1.size);
    }
}

2.1.3 二叉查找树(二叉排序树)

定义:
(1) 若左子树不为空,则左子树上所有结点的值均不大于根结点的值
(2) 若右子树不为空,则右子树上所有节点的值均不小于根结点的值
(3) 左右子树也都是二叉排序树

时间复杂度: 查找插入删除都是 O(h) h是树的高度,h∈[log n,n]

定义二叉树的结点类

public class BinTreeNode {
    private Integer data;        //数据域
    private BinTreeNode lChild; //左孩子
    private BinTreeNode rChild; //右孩子

    public BinTreeNode() { }

    public BinTreeNode(Integer data) {
        this.data = data;
    }

    public BinTreeNode(Integer data, BinTreeNode lChild, BinTreeNode rChild) {
        this.data = data;
        this.lChild = lChild;
        this.rChild = rChild;
    }

    public Integer getData() {
        return data;
    }

    public void setData(Integer data) {
        this.data = data;
    }

    public BinTreeNode getlChild() {
        return lChild;
    }

    public void setlChild(BinTreeNode lChild) {
        this.lChild = lChild;
    }

    public BinTreeNode getrChild() {
        return rChild;
    }

    public void setrChild(BinTreeNode rChild) {
        this.rChild = rChild;
    }
}

实现二叉排序树

public class BSTree {
    public BinTreeNode root;

    public BSTree() {
    }
    public BSTree(BinTreeNode root) {
        this.root = root;
    }

    //中序遍历二叉树
    public void inOrder(){
        inOrderRecursion(this.root);
        System.out.println();
    }
    //中序遍历的递归算法
    private void inOrderRecursion(BinTreeNode rt){
        if(rt == null) return;  //递归基,空树直接返回
        inOrderRecursion(rt.getlChild()); //遍历左子树
        System.out.print(rt.getData()+" ");  //访问根结点
        inOrderRecursion(rt.getrChild()); //遍历右子树
    }
    /**
     * 二叉排序树查找算法  递归实现
     * 查找树为空 或 k=结点域值,返回该结点
     * k<结点域值,则在左子树查找
     * k>结点域值,则在右子树查找
     */
    private BinTreeNode binTSearchRecursion(BinTreeNode rt, Integer key) {
        if (rt == null || rt.getData() == key) {    //等于
            return rt;
        }
        if (key < rt.getData()) {
            return binTSearchRecursion(rt.getlChild(),key);  //小于
        }
        return binTSearchRecursion(rt.getrChild(),key);      //大于
    }

    /**
     * 二叉排序树查找算法  非递归实现
     */
    private BinTreeNode binTSearch(BinTreeNode rt, Integer key) {
        while (rt != null && rt.getData() != key) {
            if (key < rt.getData()) {
                rt = rt.getlChild();
            } else {
                rt = rt.getrChild();
            }
        }
        return rt;
    }

    //二叉查找树中最小元素:从根节点向左不断深入,直到到达最左的叶子结点
    private BinTreeNode minBST(BinTreeNode rt) {
        if(rt != null){
            while (rt.getlChild() != null) {
                rt = rt.getlChild();
            }
        }
        return rt;
    }
    //二叉查找树中最大元素:从根节点向右不断深入,直到到达最右的叶子结点
    private BinTreeNode maxBST(BinTreeNode rt) {
        if(rt != null){
            while (rt.getrChild() != null) {
                rt = rt.getrChild();
            }
        }
        return rt;
    }

    /**
     * 二叉排序树插入算法
     * 从根节点逐层深入,若新节点关键字小,向左子树深入,否则向右子树深入;
     * 直到向某个结点的左子树深入而其左子树为空,或向某个结点的右子树深入而其右子树为空时,则确定了新节点的插入位置
     */
    private void insertBST(Integer key) {
        BinTreeNode p = null;
        BinTreeNode current = root;

        while (current != null) {             //找到待插入位置
            p = current;
            if (key < current.getData()) {
                current = current.getlChild();
            } else {
                current = current.getrChild();
            }
        }

        if (p == null) {
            root = new BinTreeNode(key);      //树为空
        } else if (key < p.getData()) {
            p.setlChild(new BinTreeNode(key));
        } else {
            p.setrChild(new BinTreeNode(key));
        }
    }

    /**
     * 二叉排序树删除算法
     * case1:结点T只有左子树或只有右子树或为叶子结点,当结点是左孩子时,只要令子树为其双亲结点的左子树即可;否则,令其子树为其双亲结点的右子树即可(T为叶子结点的情况也包含进去了)。
     * case2: 结点T既有左子树又有右子树,先用中序序列中结点T的前驱或后继替换T,然后删除其前驱或后继结点即可,此时的T的前驱或后继结点必然是没有右孩子或左孩子的结点。
     */
    private Integer removeBST(Integer key) {
        if (root == null || key == null) {
            return null;
        }
        return removeBST(root, key, null);
    }
    private Integer removeBST(BinTreeNode T, Integer key, BinTreeNode parent) {
        if (T == null) {
            return null;
        }

        if (key < T.getData()) {
            return removeBST(T.getlChild(), key, T);
        }
        if (key > T.getData()) {
            return removeBST(T.getrChild(), key, T);
        }

        //key==T.data时

        //左右子树都不为空
        if (T.getlChild() != null && T.getrChild() != null) {
            BinTreeNode next = T.getrChild();         //找到待删除结点T的后继结点
            while (next.getlChild() != null) {
                next = next.getlChild();
            }
            int tmp = T.getData();                    //交换T与后继结点的值
            T.setData(next.getData());
            next.setData(tmp);
            return removeBST(T.getrChild(), key, T);  //删除后继结点(此时后继结点必然没有左孩子)
        }

        //T是根结点 度为1
        if (parent == null) {
            if (T.getlChild() != null) {
                root = T.getlChild();
            } else {
                root = T.getrChild();
            }
            return T.getData();
        }

        //判断T是左子结点还是右子结点
        if (T == parent.getlChild()) {
            if (T.getlChild() != null) {
                parent.setlChild(T.getlChild());
            } else {
                parent.setlChild(T.getrChild());
            }
        } else {
            if (T.getlChild() != null) {
                parent.setrChild(T.getlChild());
            } else {
                parent.setrChild(T.getrChild());
            }
        }
        return T.getData();
    }

    /**
     * 测试
     */
    public static void main(String[] args) {
		/*----------------------
					 49
				12		    65
			 11     35    50
				  15		60
		 -----------------------*/
        //创建一个二叉树(由叶子结点往上依次添加,最后是根结点)
        BinTreeNode N11 = new BinTreeNode(11);
        BinTreeNode N15 = new BinTreeNode(15);
        BinTreeNode N60 = new BinTreeNode(60);
        BinTreeNode N35 = new BinTreeNode(35, N15, null);
        BinTreeNode N12 = new BinTreeNode(12, N11, N35);
        BinTreeNode N50 = new BinTreeNode(50, null, N60);
        BinTreeNode N65 = new BinTreeNode(65, N50, null);
        BinTreeNode N49 = new BinTreeNode(49, N12, N65);

        BSTree tree = new BSTree(N49);
        BinTreeNode root = tree.root;

        System.out.print("中根遍历:");
        tree.inOrder();

        System.out.println(tree.binTSearch(root,20));
        tree.insertBST(25);
        System.out.println(tree.removeBST(49));

        System.out.print("中根遍历:");
        tree.inOrder();

        System.out.println("创建一个空树:");
        tree = new BSTree();
        for (int i = 1; i <= 10; i++) {
            tree.insertBST(i);
        }
        System.out.print("中根遍历:");
        tree.inOrder();
    }
}

2.2 排序

在这里插入图片描述

public class MySort {

    //插入类排序:基本思想是逐个考察每个待排序元素,将每个新元素插入到前面已经排好序的序列中适当的位置上,使得新序列仍然是一个有序序列
    
    /**
     * 直接插入排序:从第二个元素开始直到第n个元素,逐个向有序序列中执行插入操作
     * 一般来说在含有j-1个元素的有序序列中插入一个元素的方法是:从第j-1个元素开始依次向前搜索应当插入的位置,同时向后右移元素
     * 时间复杂度:O(n2)
     * 空间复杂度:O(1)
     * 稳定的排序算法
     */
    public static int[] insertSort(int[] arr) {
        if (arr.length == 0) {
            return arr;
        }
        int i, j ,temp;
        for (i = 1; i < arr.length; i++) {
            temp = arr[i];				//暂存待插入数据
            for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
                arr[j + 1] = arr[j];	//将前面比arr[i]大的数据向后移动
            }
            arr[j + 1] = temp;			//插入到j+1的位置
        }
        return arr;
    }

    /**
     * 希尔排序:将待排序的元素分为多个子序列,对各个子序列分别进行直接插入排序
     * 选择一个增量序列;按增量序列个数k,对待排序元素序列进行k趟排序;每趟排序,根据对应的增量d,将待排序列分割成d个子序列,分别对各子序列进行直接插入排序
     * 时间复杂度:(目前尚未彻底解决)
     * 空间复杂度:O(1)
     * 不稳定的排序算法
     */
    public static int[] shellSort(int[] arr, int[] d) { //d[]为增量数组  (最后一个增量值必须是1)
        if (arr.length == 0) {
            return arr;
        }
        int i, j, temp;
        for (int k = 0; k < d.length; k++) {   //k趟排序,k为步长序列长度
            int dk = d[k];
            for (i = dk; i < arr.length; i++) {     //直接插入排序算法
                temp = arr[i];
                for (j = i - dk; j >= 0 && arr[j] > temp; j -= dk) {
                    arr[j + dk] = arr[j];
                }
                arr[j + dk] = temp;
            }
        }
        return arr;
    }

    //交换类排序:通过两两比较待排元素的关键字,若发现与排序要求相逆,则交换之。
    
    /**
     * 冒泡排序
     * 第1趟:将n个元素的第一个和第二个进行比较,如果两个元素位置为逆序,则交换两个元素;进而比较第二个和第三个元素,直至比较第n-1个元素和第n个元素。经过上述过程将关键字最大的元素放到了最后一个位置
     * 第2-n趟:以此类推,对前n-i+1个元素进行比较,使得前n-i+1个元素中关键字最大的元素被放到第n-i+1个位置
     * 时间复杂度:O(n2)
     * 空间复杂度:O(1)
     * 稳定的排序算法
     */
    public static int[] bubbleSort(int[] arr) {
        if (arr.length == 0) {
            return arr;
        }
        int i, j;
        for (i = 1; i < arr.length; i++) {
            for (j = 0; j < arr.length-i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        return arr;
    }

    /**
     * 快速排序:通过基准数x将序列一分为二,左子序列元素均小于x,右子序列元素均大于x;递归的对左右子序列排序
     * 时间复杂度:平均:O(n log n), 最坏为O(n2)
     * 时间复杂度分析:一次划分算法从两头交替搜索,直到low与high重合,因此时间复杂度是O(n)
     * 理想情况,每次划分所选择的中间数恰好将当前序列几乎等分,经过log n趟划分便可得到长度为1的子表;最坏的情况,每次所选的中间数是当前序列的最大或最小元素,使得每次划分所得子表中一个为空表,另一个为原表的长度-1,这样需要经过n趟划分
     * 空间复杂度:O(log n), 最坏为O(n)  (需要栈空间实现递归)
     * 不稳定的排序算法
     */
    public static int[] quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pivotLoc = partition(arr, low, high);
            quickSort(arr, low, pivotLoc-1);	//低子表递归排序
            quickSort(arr, pivotLoc+1, high);	//高字表递归排序
        }
        return arr;
    }
    //一趟快速排序
    private static int partition(int[] arr, int i, int j) {
        int pivot = arr[i];		//暂存基准数据
        while (i != j) {
            while (i < j && arr[j] >= pivot) 
                j--;			//从后遍历查找小于基准的数据
            arr[i] = arr[j];
            while (i < j && arr[i] <= pivot) 
                i++;			//从前遍历查找大于基准的数据
            arr[j] = arr[i];
        }
        arr[i] = pivot;
        return i;
    }

    //选择类排序:每一趟从n-i+1个元素中选取一个关键字最小的元素作为有序序列中第i个元素
    /**
     * 简单选择排序:第k趟,从第k个元素开始的n-k+1个元素中选出关键字最小的元素与第k个元素交换
     * 时间复杂度:O(n2)
     * 空间复杂度:O(1)
     * 不稳定的排序算法
     */
    public static int[] selectSort(int[] arr) {
        if (arr.length == 0) {
            return arr;
        }
        for (int i = 0; i < arr.length; i++) {
            int min = i;
            for (int j = i+1; j < arr.length; j++) {
                if (arr[j] < arr[i])
                    min = j;
            }
            int temp = arr[i];
            arr[i] = arr[min];
            arr[min] = temp;
        }
        return arr;
    }

    /**
     * 堆排序
     * 1、将待排序的序列构造成一个大顶堆(完全二叉树,每个节点的值都大于或等于其子节点的值),根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
     * 2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
     * 3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,得到一个有序的序列了。
     *
     * 构建大顶堆:找到所有非叶子节点(叶子节点可以看作已符合堆要求的节点),调整它们的父子关系,从下往上遍历
     * 时间复杂度:O(n log n)
     * 空间复杂度:O(1)
     * 不稳定的排序算法
     */
    public static int[] heapSort(int[] arr) {
        if (arr == null || arr.length == 0) {
            return arr;
        }
        int len = arr.length;
        // 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
        for (int i = (int)Math.floor(len / 2) - 1; i >= 0; i--) {  //完全二叉树,第一个非叶子节点的索引为arr.length/2-1。
            heapAdjust(arr, i, len);
        }
        // 交换堆顶和当前末尾的节点,重置大顶堆
        for (int i = len - 1; i > 0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            heapAdjust(arr, 0, i);
        }
        return arr;
    }
    private static void heapAdjust(int[] arr, int i, int len) {
        // 先根据堆性质,找出当前节点左右子节点的索引
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        // 默认当前节点(父节点)是最大值。
        int largestIndex = i;
        if (left < len && arr[left] > arr[largestIndex]) {
            // 如果有左节点,并且左节点的值更大,更新最大值的索引
            largestIndex = left;
        }
        if (right < len && arr[right] > arr[largestIndex]) {
            // 如果有右节点,并且右节点的值更大,更新最大值的索引
            largestIndex = right;
        }

        if (largestIndex != i) {
            // 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
            int temp = arr[largestIndex];
            arr[largestIndex] = arr[i];
            arr[i] = temp;
            // 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
            heapAdjust(arr, largestIndex, len);
        }
    }

    /**
     * 归并排序:将待排序的序列划分为大小相等(或大致相等)的两个子序列;递归划分直至子序列规模为1;将两个有序的子序列合并为一个有序序列
     * 时间复杂度:O(n log n)
     * 空间复杂度:O(n)
     * 稳定的排序算法
     */
    public static int[] mergeSort(int[] arr) {
        sort(arr, 0, arr.length - 1);
        return arr;
    }
    //将arr序列的[L R]序列排序
    public static void sort(int[] arr, int L, int R) {
        if(L == R) {
            return;
        }
        int mid = L + ((R - L) >> 1);
        sort(arr, L, mid);          //左子序列排序
        sort(arr, mid + 1, R);  //右子序列排序
        merge(arr, L, mid, R);      //合并两段已排好序的序列成一个排序数组
    }
    //将两段排序好的数组结合成一个排序数组
    private static void merge(int[] arr, int L, int mid, int R) {
        int[] temp = new int[R-L+1];
        int i = 0;
        int p1 = L;
        int p2 = mid + 1;
        // 比较左右两部分的元素,哪个小,把那个元素填入temp中
        while(p1 <= mid && p2 <= R) {
            temp[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        // 上面的循环退出后,把剩余的元素依次填入到temp中
        // 以下两个while只有一个会执行
        while(p1 <= mid) {
            temp[i++] = arr[p1++];
        }
        while(p2 <= R) {
            temp[i++] = arr[p2++];
        }
        // 把最终的排序的结果复制给原数组
        for(i = 0; i < temp.length; i++) {
            arr[L + i] = temp[i];
        }
    }
    /**
     * 测试
     */
    public static void main(String[] args) {
        int[] arr = {26,53,48,11,13,48,32,15};
        int[] brr = MySort.insertSort(arr);
        System.out.println("直接插入排序 "+Arrays.toString(brr));

        int[] d = {5, 3, 1}; //增量序列
        brr = MySort.shellSort(arr, d);
        System.out.println("希尔排序 "+Arrays.toString(brr));

        brr = MySort.bubbleSort(arr);
        System.out.println("冒泡排序 "+Arrays.toString(brr));

        brr = MySort.quickSort(arr, 0, arr.length-1);
        System.out.println("快速排序 "+Arrays.toString(brr));

        brr = MySort.selectSort(arr);
        System.out.println("简单选择排序 "+Arrays.toString(brr));

        brr = MySort.heapSort(arr);
        System.out.println("堆排序 "+Arrays.toString(brr));

        brr = MySort.mergeSort(arr);
        System.out.println("归并排序 "+Arrays.toString(brr));
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值