Java算法体系学习(二)几种基本的数据结构和应用(链表反转、栈模拟队列、队列模拟栈、最小栈等)

二、一些基本的数据结构及操作

1、链表数据结构

  • 包括一个数值域和指向下一个节点的引用
package list;

/**
 * 单向链表,包括一个value域和指向下一个节点的指针
 * @param <T>
 */
public class Node<T> {
    public T value;
    public Node<T> next;

    public Node(T value) {
        this.value = value;
        this.next = null;
    }
}

2、对一个链表进行翻转

  • 思路:新建立一个链表,对原链表依次遍历每个位置采用头插法插入到新链表的头部,最后返回新链表的头,采用三个变量。
    • pre :新链表的头结点
    • head:原链表的头结点
    • next:原链表下一个待翻转节点

在这里插入图片描述

package list;

public class ReverseList {
    /**
     * 对链表进行翻转,返回翻转后的头结点
     * @param head  原链表的头结点
     * @param <T>   泛型参数
     * @return      翻转后链表的头结点
     */
    public  static <T>  Node<T> reverseList(Node<T> head){
        //做一些边界处理
        if(head == null || head.next == null){
            return head;
        }

        //pre   新链表的头结点
        Node<T> pre = null;
        //next 记录原链表的下一个待翻转节点
        Node<T> next = null;

        while (head != null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }

        return pre;
    }
}

  • 双向链表同理,只需要在更改一个last指针即可

3、给定一个值,删除链表中与该值相同的所有节点

  • 必须带有返回值,因为可能删除第一个节点
package list;

public class DeleteElement {
    /**
     * 删除链表中所有值等于value的节点
     * @param head      给定链表的头节点
     * @param value     给定目标数值
     * @param <T>       泛型参数
     * @return          删除元素后的头节点
     */
    public static <T> Node<T> deleteValue(Node<T> head, T value) {
        //边界处理
        if (head == null) {
            return head;
        }

        //使head来到第一个不需要删除的位置
        while (head != null) {
            if (!head.value.equals(value)) {
                break;
            }
            head = head.next;
        }

        //1.对于head == null,即链表全部删除,这是返回head 直接为null
        //2.对于head != null,从该位置开始,遍历整个链表,删除对于节点
        //删除某个位置元素需要前一个节点
        Node<T> pre = head;
        Node<T> cur = head;
        while (cur != null) {
            if (cur.value.equals(value)) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
            cur = cur.next;
        }
        return head;
    }
}

4、用数组实现栈

  • 栈:先进后出,加入和弹出都是在栈顶
  • push、pop、peek都是常数操作
package stack;

public class ArrayToStack<V> {
    private V[] data;
    private int size;

    public ArrayToStack(int size) {
        data = (V[]) new Object[size];
        this.size = 0;
    }

    /**
     * 栈是否为空
     * @return  空为true
     */
    public boolean isEmpty(){
        return size == 0;
    }

    /**
     * 栈是否已满
     * @return  是为true
     */
    public boolean isFull(){
        return data.length == size;
    }

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

    /**
     * 元素入栈
     * @param value 待插入元素
     * @return  插入成功返回true,若栈已经满返回false
     */
    public boolean push(V value){
        if(!isFull()){
            data[size++] = value;
            return true;
        }

        return false;
    }

    /**
     * 从栈顶弹出元素,栈为空则抛出异常
     * @return  栈顶元素
     */
    public V pop() throws Exception {
        if(isEmpty()){
            throw  new Exception("当前栈为空......");
        }
        return data[--size];
    }

    /**
     * 返回栈顶元素不弹出
     * @return  栈顶元素
     */
    public V peek() throws Exception {
        if(isEmpty()){
            throw  new Exception("当前栈为空......");
        }
        return data[size - 1];
    }
}

5、用已有栈结构实现pop、push、getMin都是常数的操作

  • getMIn为弹出当前栈的最小元素
  • 思路:准备两个栈,一个正常栈,一个最小栈。每次待加入元素若比最小栈顶的元素小则两个栈都加入该元素,若待加入元素大与最小栈顶元素则最小栈仍然加入它的栈顶元素,这样保证最小栈永远为当前栈里的最小元素。
package stack;

import java.util.Stack;

public class MinStack{
    private Stack<Integer> stack;
    private Stack<Integer>  minStack;

    public MinStack() {
        this.stack = new Stack<>();
        this.minStack = new Stack<>();
    }

    public Integer getMin() throws Exception {
        if(minStack.isEmpty()){
            throw  new Exception("当前栈为空......");
        }
        return minStack.peek();
    }

    public Integer pop(){
        minStack.pop();
        return stack.pop();
    }

    public void push(Integer value) throws Exception {
        if(stack.isEmpty()){
            minStack.push(value);
        }else if(value < getMin()){
            minStack.push(value);
        }else {
            minStack.push(minStack.peek());
        }
        stack.push(value);
    }
}

6、用数组实现队列

  • 循环利用数组实现队列
  • 加入两个辅助遍历pushIndex和popIndex记录下次出队和入队的位置,这样不用每次都进行判断
package queue;

public class ArrayToQueue<V> {
    //存储真实元素
    private V[] data;
    //存储队列的大小
    private int size;
    //队列的最大容量
    private int limit;
    //下一次待插入位置
    private int pushIndex;
    //下一次弹出位置
    private int pollIndex;

    public ArrayToQueue(int limit) {
        this.limit = limit;
        this.size = 0;
        data = (V[]) new Object[limit];
        pollIndex = 0;
        pushIndex = 0;
    }

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

    public boolean isFull() {
        return size == limit;
    }

    public void push(V value) throws Exception {
        if (isFull()) {
            throw new Exception("当前队列已满......");
        }
        size++;
        data[pushIndex] = value;
        //每次只能从数组头部出队,尾部入队列,所有当尾部满后但头部还有位置就可以在头部进行插入
        pushIndex = (pushIndex + 1) % limit;
    }

    public V poll() throws Exception {
        if (isEmpty()) {
            throw new Exception("当前队列为空......");
        }
        size--;
        V ans = data[pollIndex];
        pollIndex = (pollIndex + 1) % limit;
        return ans;
    }
}

7、用队列实现栈

  • 队列先进先出,栈先进后出
  • 思路:两个队列。一个queue,一个help。每次元素进入help队列,这时候把queue队列的元素依次插入help队列尾部,在互换queue和help的引用。

在这里插入图片描述

package stackandqueue;

import java.util.LinkedList;
import java.util.Queue;

public class QueueToStack <V>{
    private Queue<V> queue;
    private Queue<V> help;

    public QueueToStack() {
        queue = new LinkedList<>();
        help = new LinkedList<>();
    }

    public void push(V value){
        help.offer(value);
        while (!queue.isEmpty()){
            help.offer(queue.poll());
        }
        Queue<V> tmp = queue;
        queue = help;
        help = tmp;
    }

    public V poll(){
        return queue.poll();
    }

}


  • 上面做法是在每次加入时进行元素转换,下面还可以用在弹出时进行元素移动
package stackandqueue;

import java.util.LinkedList;
import java.util.Queue;

public class QueueToStackChange {
    public class QueueToStack<V> {
        private Queue<V> queue;
        private Queue<V> help;

        public QueueToStack() {
            queue = new LinkedList<>();
            help = new LinkedList<>();
        }

        public void push(V value) {
            queue.offer(value);
        }

        public V poll() {
            while (queue.size() > 1) {
                help.offer(queue.poll());
            }

            V ans = queue.poll();
            Queue<V> tmp = queue;
            queue = help;
            help = tmp;
            return ans;
        }
    }
}

8、用栈实现队列

  • 栈:先进后出
  • 队列:先进先出
  • 思路:用两个栈,一个push栈,一个pop栈。需要加入时元素从push加入,弹出时检查pop栈是否为空,不空直接返回pop栈顶元素,空的话需要将push栈的元素弹到pop中再从pop中弹出,感兴趣可以自己画图理解,这里不再画图。
package stackandqueue;

import java.util.Stack;

public class StackToQueue<V> {
    private Stack<V> pushStack;
    private Stack<V> popStack;

    public StackToQueue() {
        popStack = new Stack<>();
        pushStack = new Stack<>();
    }

    public boolean isEmpty(){
        return pushStack.isEmpty() && popStack.isEmpty();
    }


    public void push(V value){
        pushStack.push(value);
    }

    public V poll() throws Exception {
        if(isEmpty()){
            throw new Exception("当前队列为空......");
        }

        if(popStack.isEmpty()){
            while (!pushStack.isEmpty()){
                popStack.push(pushStack.pop());
            }
        }

        return popStack.pop();
    }

}

9、Master公式(计算递归时间复杂度)

  • 对于类似 T(N) = aT(N/b) + O(Nd) 的递推形式而言(即子问题规模一致)
  1. log(ba) < d ,O(Nd)
  2. log(ba) > d ,O(Nlog(ba))
  3. log(ba) = d ,O(NdlogN)

10、哈希表和有序表

10.1 哈希表

  • 增删改查都是O(1)操作
  • 哈希表的基础类型:String、基础类型及其包装类在Hash表下按值传递

10.2 有序表

  • 在Java中为一个接口:TreeMap(用红黑树实现)
  • 红黑树、avl、sb树、跳表N
  • CRUD时间复杂度O(logN)
  • 自定义类型需要重写排序算法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值