【算法&数据结构体系篇class03】:数组、链表、栈、队列、递归时间复杂度、哈希表、有序表问题

本文介绍了链表的反转方法,包括单链表和双链表,以及用集合验证反转的正确性。此外,展示了如何使用双向链表实现栈和队列,并提供了基于数组的队列实现。还讨论了通过两个栈获取最小值和实现队列的策略。最后,文章提到了Master公式在计算递归函数时间复杂度中的应用,以及哈希表和有序表的特性、操作及时间复杂度分析。
摘要由CSDN通过智能技术生成

一、反转链表

package class03;

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

/**
 * 链表反转
 */
public class ReverseLinkedList {
    public static class Node {
        public int value;
        public Node next;

        public Node(int data) {
            value = data;
        }
    }

    public static class DoubleNode {
        public int value;
        public DoubleNode last;
        public DoubleNode next;

        public DoubleNode(int data) {
            value = data;
        }
    }

    //  head 单链表反转
    //   a    ->   b    ->  c  ->  null
    //   c    ->   b    ->  a  ->  null
    public static Node reverseLinkedList(Node head) {
        Node pre = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    //双链表反转
    public static DoubleNode reverseDoubleList(DoubleNode head) {
        DoubleNode pre = null;
        DoubleNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            head.last = next;
            pre = head;
            head = next;
        }
        return pre;
    }

    //用集合来实现单链表反转,用于进行对比验证
    public static Node testReverseLinkedList(Node head) {
        if (head == null) return null;
        ArrayList<Node> list = new ArrayList<>();
        while (head != null) {
            list.add(head);
            head = head.next;
        }
        list.get(0).next = null;
        for (int i = 1; i < list.size(); i++) {
            list.get(i).next = list.get(i - 1);
        }
        return list.get(list.size() - 1);
    }

    //用集合来实现双链表反转,用于对比验证
    public static DoubleNode testReverseDoubleList(DoubleNode head) {
        if (head == null) return null;
        ArrayList<DoubleNode> list = new ArrayList<>();
        while (head != null) {
            list.add(head);
            head = head.next;
        }
        list.get(0).next = null;
        DoubleNode pre = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            DoubleNode cur = list.get(i);
            cur.last = null;
            cur.next = pre;
            pre.last = cur;
            pre = cur;
        }
        return list.get(list.size() - 1);

    }

    // for test 随机生成单链表
    public static Node generateRandomLinkedList(int len, int value) {
        int size = (int) (Math.random() * (len + 1));
        if (size == 0) return null;
        Node head = new Node((int) (Math.random() * (value + 1)));
        Node pre = head;
        size--;
        while (size != 0) {
            Node cur = new Node((int) (Math.random() * (value + 1)));
            pre.next = cur;
            pre = cur;
            size--;
        }
        return head;
    }

    // for test 随机生成双链表
    public static DoubleNode generateRandomDoubleList(int len, int value) {
        int size = (int) (Math.random() * (len + 1));
        if (size == 0) return null;
        DoubleNode head = new DoubleNode((int) (Math.random() * (value + 1)));
        DoubleNode pre = head;
        size--;
        while (size != 0) {
            DoubleNode cur = new DoubleNode((int) (Math.random() * (value + 1)));
            pre.next = cur;
            cur.last = pre;
            pre = cur;
            size--;
        }
        return head;
    }
    // for test 集合存储链表节点值
    public static List<Integer> getLinkedListOriginOrder(Node head) {
        List<Integer> ans = new ArrayList<>();
        while (head != null) {
            ans.add(head.value);
            head = head.next;
        }
        return ans;
    }

    // for test 验证链表节点值是否反转成功
    public static boolean checkLinkedListReverse(List<Integer> origin, Node head) {
        for (int i = origin.size() - 1; i >= 0; i--) {
            if (!origin.get(i).equals(head.value)) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

    // for test
    public static List<Integer> getDoubleListOriginOrder(DoubleNode head) {
        List<Integer> ans = new ArrayList<>();
        while (head != null) {
            ans.add(head.value);
            head = head.next;
        }
        return ans;
    }

    // for test 验证双链表节点值是否反转成功
    public static boolean checkDoubleListReverse(List<Integer> origin, DoubleNode head) {
        DoubleNode end = null;
        for (int i = origin.size() - 1; i >= 0; i--) {
            if (!origin.get(i).equals(head.value)) {
                return false;
            }
            end = head;
            head = head.next;
        }
        for (int i = 0; i < origin.size(); i++) {
            if (!origin.get(i).equals(end.value)) {
                return false;
            }
            end = end.last;
        }
        return true;
    }

    // head = removeValue(head, 2);  删除链表中值为2的节点。 1->3->2->2->4->2->5  =》   1->3->4->5
    public static Node removeValue(Node head, int num) {
        //head来到第一个不需要删的位置 先确认头节点,排除排前面如果是num 则需一次跳过,比如 2->2->2->2->1->3   需要将新的头节点head指向到1的位置
        while(head != null){
            if(head.value != num){
                break;
            }
            head = head.next;
        }
        // 1 ) head == null
        // 2 ) head != null
        Node pre =head,cur=head;
        while (cur != null){
            if(cur.value ==num){
                pre.next = cur.next;
            }else {
                pre = cur;
            }
            cur = cur.next;
        }
        return head;
    }

    // for test
    public static void main(String[] args) {
        int len = 50;
        int value = 100;
        int testTime = 100000;
        System.out.println("test begin!");
        for (int i = 0; i < testTime; i++) {
            Node node1 = generateRandomLinkedList(len, value);
            List<Integer> list1 = getLinkedListOriginOrder(node1);
            node1 = reverseLinkedList(node1);
            if (!checkLinkedListReverse(list1, node1)) {
                System.out.println("Oops1!");
            }

            Node node2 = generateRandomLinkedList(len, value);
            List<Integer> list2 = getLinkedListOriginOrder(node2);
            node2 = testReverseLinkedList(node2);
            if (!checkLinkedListReverse(list2, node2)) {
                System.out.println("Oops2!");
            }

            DoubleNode node3 = generateRandomDoubleList(len, value);
            List<Integer> list3 = getDoubleListOriginOrder(node3);
            node3 = reverseDoubleList(node3);
            if (!checkDoubleListReverse(list3, node3)) {
                System.out.println("Oops3!");
            }

            DoubleNode node4 = generateRandomDoubleList(len, value);
            List<Integer> list4 = getDoubleListOriginOrder(node4);
            node4 = testReverseDoubleList(node4);
            if (!checkDoubleListReverse(list4, node4)) {
                System.out.println("Oops4!");
            }

        }
        System.out.println("test finish!");
    }
}

二、双向链表实现栈和队列

package class03;

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


//双向链表实现队列和栈
public class DoubleEndsQueueToStackAndQueue {
    public static class Node<T> {
        public T value;
        public Node<T> last;
        public Node<T> next;

        public Node(T data) {
            value = data;
        }
    }

    public static class DoubleEndQueue<T> {
        public Node<T> head;
        public Node<T> tail;

        //头端添加节点
        public void addFromHead(T value) {
            Node<T> cur = new Node<>(value);
            if (head == null) {
                head = cur;
                tail = cur;
            } else {
                cur.next = head;
                head.last = cur;
                head = cur;
            }
        }

        //尾端添加节点
        public void addFromBottom(T value) {
            Node<T> cur = new Node<>(value);
            if (head == null) {
                head = cur;
                tail = cur;
            } else {
                cur.last = tail;
                tail.next = cur;
                tail = cur;
            }
        }

        //弹出头端节点
        public T popFromHead() {
            if (head == null) {
                return null;
            }
            Node<T> cur = head;
            if (head == tail) {
                head = null;
                tail = null;
            } else {
                head = head.next;
                head.last = null;
                cur.next = null;
            }
            return cur.value;
        }

        //弹出尾端节点
        public T popFromBottom() {
            if (head == null) {
                return null;
            }
            Node<T> cur = tail;
            if (head == tail) {
                head = null;
                tail = null;
            } else {
                tail = tail.last;
                tail.next = null;
                cur.last = null;
            }
            return cur.value;
        }

        //判断是否为空
        public boolean isEmpty() {
            return head == null;
        }
    }

    //构建栈结构
    public static class MyStack<T> {
        //双向链表结构作为属性
        private DoubleEndQueue<T> queue;

        //构造器
        public MyStack() {
            queue = new DoubleEndQueue<>();
        }

        //入栈
        public void push(T data) {
            queue.addFromHead(data);
        }

        //出栈
        public T pop() {
            return queue.popFromHead();
        }

        //是否为空
        public boolean isEmpty() {
            return queue.isEmpty();
        }
    }

    //构造队列
    public static class MyQueue<T> {
        private DoubleEndQueue<T> queue;

        public MyQueue() {
            queue = new DoubleEndQueue<>();
        }

        public void push(T data) {
            queue.addFromHead(data);
        }

        public T poll() {
            return queue.popFromBottom();
        }

        public boolean isEmpty() {
            return queue.isEmpty();
        }
    }

    //判断两个Integer节点是否相等
    public static boolean isEqual(Integer o1, Integer o2) {
        if (o1 == null && o2 == null) return true;
        if (o1 == null && o2 != null) return false;
        if (o1 != null && o2 == null) return false;
        return o1.equals(o2);
    }

    public static void main(String[] args) {
        int oneTestDataNum = 100;
        int value = 10000;
        int testTimes = 100000;
        for (int i = 0; i < testTimes; i++) {
            Code03_DoubleEndsQueueToStackAndQueue.MyStack<Integer> myStack = new Code03_DoubleEndsQueueToStackAndQueue.MyStack<>();
            Code03_DoubleEndsQueueToStackAndQueue.MyQueue<Integer> myQueue = new Code03_DoubleEndsQueueToStackAndQueue.MyQueue<>();
            Stack<Integer> stack = new Stack<>();
            Queue<Integer> queue = new LinkedList<>();
            for (int j = 0; j < oneTestDataNum; j++) {
                int nums = (int) (Math.random() * value);
                if (stack.isEmpty()) {
                    myStack.push(nums);
                    stack.push(nums);
                } else {
                    if (Math.random() < 0.5) {
                        myStack.push(nums);
                        stack.push(nums);
                    } else {
                        if (!isEqual(myStack.pop(), stack.pop())) {
                            System.out.println("oops!");
                        }
                    }
                }
                int numq = (int) (Math.random() * value);
                if (queue.isEmpty()) {
                    myQueue.push(numq);
                    queue.offer(numq);
                } else {
                    if (Math.random() < 0.5) {
                        myQueue.push(numq);
                        queue.offer(numq);
                    } else {
                        if (!isEqual(myQueue.poll(), queue.poll())) {
                            System.out.println("oops!");
                        }
                    }
                }
            }
        }
        System.out.println("finish!");
    }
}

三、数组实现队列

(数组实现栈就比较简单 因为都是入栈出栈只需在一个方向,下一个index索引加1就能满足,其他逻辑类似,而队列,需要判断是index+1还是回到0索引位置)

package class03;

/**
 * 
 */
//数组实现队列, 栈就比较简单 入栈就直接按需从0索引赋值,出栈也是从最后一个索引arr.length-1开始弹出
//队列就是先进先进,入栈从0开始,出栈从0开始,而且中间出栈后,0位置空,接着要入栈就得判断下一个位置
// 是否超边界,是否超大小,如果超大小就肯定满了,如果没有超,就看下个位置会不会越界,会就需要入栈到0位置,
// 形成一个环形循环数组

public class RingArray {
    public static class MyQueue {
        private int[] arr;
        private int pushi; //随着插入,这个索引就往下,所以表示是 元素边界的end边界
        private int polli; //begin
        private int size;  //存在数组中的元素个数
        private final int limit; //数组的长度设置为常量 不可修改

        public MyQueue(int limit) {
            arr = new int[limit];
            pushi = 0;
            polli = 0;
            size = 0;
            this.limit = limit;
        }

        public void push(int value) {
            //先判断size元素实际个数是否与数组长度相等 相等就不能入队列了
            if (size == limit) {
                throw new RuntimeException("队列满,不能添加元素!");
            }
            size++;
            //赋值
            arr[pushi] = value;
            //判断下个索引
            pushi = nextIndex(pushi);

        }

        public int poll() {
            //先判断size元素实际个数是否为0,0则没有元素 不能出队列
            if (size == 0) {
                throw new RuntimeException("队列空,不能弹出元素!");
            }
            size--;
            //返回弹出元素,该索引位置的内容无需重新赋值,因为下次元素push到该位置会赋值覆盖的
            int ans = arr[polli];
            //判断下个索引
            polli = nextIndex(polli);
            return ans;
        }

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

        //判断元素的下个索引,需要与arr[limit-1]即数组最后一个索引比较,如果小于表示可以接着+1,
        //大于等于就需要循环走到0索引位置
        public int nextIndex(int index) {
            return index < limit - 1 ? ++index : 0;
        }
    }
}

四、通过两个栈结构,来实现获取栈中的最小值

package class03;

import java.util.Stack;

/**
 *
 */

//通过两个栈结构,来实现获取栈中的最小值
public class GetMinStack {
    public static class MyStack1 {
        private Stack<Integer> stackData;
        private Stack<Integer> stackMin;

        public MyStack1() {
            stackData = new Stack<>();
            stackMin = new Stack<>();
        }

        public void push(int num) {
            //如果当前最小值为空,那么入栈的值就定为最小值,入最小栈
            if (stackMin.isEmpty()) {
                stackMin.push(num);
            } else {
                //如果非空,而当前值小于等于最小栈的最小值,那么就入栈,大于则不用入栈。保证最小栈栈顶是最小值
                if (num <= this.getmin()) {
                    stackMin.push(num);
                }
            }
            stackData.push(num);
        }

        public int pop() {
            if (stackData.isEmpty()) {
                throw new RuntimeException("数据栈为空,没有元素!");
            }
            //数据栈出栈
            int ans = stackData.pop();
            //同时要判断是否出栈的是与当前最小栈的栈顶一样 是则需要一起出栈 才能保持始终为最小值栈顶
            if (ans == this.getmin()) {
                stackMin.pop();
            }
            return ans;

        }

        public int getmin() {
            if (stackMin.isEmpty()) {
                throw new RuntimeException("最小栈为空,没有元素!");
            }
            //非空则取栈顶元素即最小值
            return stackMin.peek();
        }
    }

    public static void main(String[] args) {
        MyStack1 stack1 = new MyStack1();
        stack1.push(3);
        System.out.println(stack1.getmin());
        stack1.push(4);
        System.out.println(stack1.getmin());
        stack1.push(1);
        System.out.println(stack1.getmin());
        System.out.println(stack1.pop());
        System.out.println(stack1.getmin());

        System.out.println("=============");
    }
}

五、两个栈实现队列,队列先进先出,栈先进后出,需要用两个栈来实现

package class03;

import java.util.Stack;

/**
 * 两个栈实现队列,队列先进先出,栈先进后出,需要用两个栈来实现
 */
public class TwoStacksImplementQueue {
    public static class TwoStacksQueue{
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;
        public TwoStacksQueue(){
            stackPush = new Stack<>();
            stackPop = new Stack<>();
        }
        public void add(int num){
            stackPush.push(num);

        }
        //出栈注意,假设
        //pop栈有数据就直接出栈。 没数据就需要将现有的push栈数据全部清空入pop栈,在出pop栈
        public int poll(){
            if(stackPop.isEmpty() && stackPush.isEmpty()){
                throw new RuntimeException("队列为空,没有元素!");
            }
            if(!stackPop.isEmpty()){
                return stackPop.pop();
            }else{
                while(!stackPush.empty())
                    stackPop.push(stackPush.pop());
            }
            return stackPop.pop();
        }
        public int peek(){
            if(stackPop.isEmpty() && stackPush.isEmpty()){
                throw new RuntimeException("队列为空,没有元素!");
            }
            if(!stackPop.isEmpty()){
                return stackPop.peek();
            }else{
                while(!stackPush.empty())
                    stackPop.push(stackPush.pop());
            }
            return stackPop.peek();
        }
    }
    public static void main(String[] args) {
        TwoStacksQueue test = new TwoStacksQueue();
        test.add(1);
        test.add(2);
        test.add(3);
        test.add(4);
        System.out.println(test.peek());
        System.out.println(test.poll());
        System.out.println(test.peek());
        System.out.println(test.poll());
        System.out.println(test.peek());
        System.out.println(test.poll());
    }
}

六、两个队列实现一个栈结构

package class03;

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

/**
 * 两个队列实现一个栈结构
 */
public class TwoQueueImplementStack {
    public static class TwoQueueStack<T> {
        private Queue<T> queue;
        private Queue<T> help;

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

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

        public T poll() {
            //把队列都弹出到help辅助队列,保留最后一个不弹,用来pop
            while (queue.size() > 1) {
                help.offer(queue.poll());
            }
            T ans = queue.poll();
            //此时queue为空队列,再把help队列数据放回来
            Queue<T> temp = queue;
            queue = help;
            help = temp;
            return ans;
        }

        public T peek() {
            //把队列都弹出到help辅助队列,保留最后一个不弹,用来pop
            while (queue.size() > 1) {
                help.offer(queue.poll());
            }
            T ans = queue.poll();
            //这里注意要把弹出的最后一个元素入回队列
            help.offer(ans);
            //此时queue为空队列,再把help队列数据放回来
            Queue<T> temp = queue;
            queue = help;
            help = temp;
            return ans;
        }

        public boolean isEmpty() {
            return queue.isEmpty();
        }
    }

    public static void main(String[] args) {
        System.out.println("test begin");
        TwoQueueStack<Integer> myStack = new TwoQueueStack<>();
        Stack<Integer> test = new Stack<>();
        int testTime = 1000000;
        int max = 1000000;
        for (int i = 0; i < testTime; i++) {
            if (myStack.isEmpty()) {
                if (!test.isEmpty()) {
                    System.out.println("Oops");
                }
                int num = (int) (Math.random() * max);
                myStack.push(num);
                test.push(num);
            } else {
                if (Math.random() < 0.25) {
                    int num = (int) (Math.random() * max);
                    myStack.push(num);
                    test.push(num);
                } else if (Math.random() < 0.5) {
                    if (!myStack.peek().equals(test.peek())) {
                        System.out.println("Oops");
                    }
                } else if (Math.random() < 0.75) {
                    if (!myStack.poll().equals(test.pop())) {
                        System.out.println("Oops");
                    }
                } else {
                    if (myStack.isEmpty() != test.isEmpty()) {
                        System.out.println("Oops");
                    }
                }
            }
        }
        System.out.println("test finish!");

    }

}

七、Master公式

形如

T(N)= a * T(N/b) + O(N^d)(其中的a、b、d都是常数)

的递归函数,可以直接通过Master公式来确定时间复杂度

如果log(b,a)< d,复杂度为O(N^d)

如果 log(b,a)> d,复杂度为O(N^log(b,a))

如果 log(b,a)== d,复杂度为O(N^d *logN)

注意:

(注意是需要两次子递归的递归元素是同等的,比如中点分开左右两数组,分别递归,此时两个子递归元素都是N/2,时间复杂度O(N/2)+O(N/2),例如归并排序)

归并排序,递归过程还需要进行合并左右数组,需要合并操作时间复杂度为O(N)

此形式符合Master公式,T(N)= 2 * T(N/2) + O(N^1)  ,a=2,b=2,d=1,

log(2,2) == 1 所以归并排序复杂度为O(N *logN)

八、哈希表

1)哈希表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据value,可以使用HashSet结构
3)如果既有key,又有伴随数据value,可以使用HashMap结构
4)有无伴随数据,是HashMap和HashSet唯一的区别,实际结构是一回事
5)使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为 O(1),但是常数时间比较大
6)放入哈希表的东西,如果是基础类型,内部按值传递,内存占用是这个东西的大小
7)放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是8字节

九、有序表

1)有序表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据value,可以使用TreeSet结构
3)如果既有key,又有伴随数据value,可以使用TreeMap结构
4)有无伴随数据,是TreeSet和TreeMap唯一的区别,底层的实际结构是一回事
5)有序表把key按照顺序组织起来,而哈希表完全不组织
6)红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
7)放入如果是基础类型,内部按值传递,内存占用就是这个东西的大小
8)放入如果不是基础类型,内部按引用传递,内存占用是8字节
9)不管是什么底层具体实现,只要是有序表,都有以下固定的基本功能和固定的时间复杂度
1)void put(K key, V value)
将一个(key,value)记录加入到表中,或者将key的记录更新成value。
2)V get(K key)
根据给定的key,查询value并返回。
3)void remove(K key)
移除key的记录。
4)boolean containsKey(K key)
询问是否有关于key的记录。
5)K firstKey()
返回所有键值的排序结果中,最小的那个。
6)K lastKey()
返回所有键值的排序结果中,最大的那个。
7)K floorKey(K key)
返回<=key 离key最近的那个
8)K ceilingKey(K key)
返回>=key 离key最近的那个

哈希表在使用时,增删改查时间复杂度都是O(1)

有序表在使用时,比哈希表功能多,时间复杂度都是O(logN)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值