class4:基础的数据结构

1.基本数据结构

1.1链表:

1.1.1单链表

第一种为int 类型数据的单链表

public static class Node{
    public Node next;
    public int value;
    public Node(int data){
        value=data;
    }
}

第二种为任意类型的链表

//study1
    public static class TNode<T>{
        public TNode<T> next;
        public T value;
        public TNode(T data)
        {
            value=data;
        }
}

1.1.2双向链表

第一种为int类型的双向链表

    public static class DoubleNode{
        public int value;
        public DoubleNode last;
        public DoubleNode next;
        public DoubleNode(int data){
            value=data;
        }
    }

第二种为泛型双向链表

    public static class DoubleTNode<T>{
        public T value;
        public DoubleTNode<T> last;
        public DoubleTNode<T> next;
        public DoubleTNode(T data){
            value=data;
        }
}

1.2队列:

1.2.1链表实现队列

ac3:单、双向链表实现队列,两种都可以进行实现。以单链表为例 v48.18

为了出队列更好的找到下一个元素,因此队列头作为链表的头结点。

单链表实现队列

package class4;/*
 *项目名: lanqiao_doexercise
 *文件名:LinkedQue
 *创建者:ZEL
 *创建时间:2024/8/3 20:53
 *描述:TODO:利用单链表实现队列
 */

public class LinkedQueue<T> {
    //构造的思路:
    private static class Node<T>{
        private T value;
        private Node<T> next;
        public Node(T data)
        {
            value=data;
        }
    }
    private Node<T> head=null;
    private Node<T> tail=null;//可以直接这样初始化吗??
//    public LinkedQueue() {
//        head = null;
//        tail = null;
//    }
//    入队操作,入队的是数据,所以入队的时候要新建节点
    public void enqueue(T value)
    {
        Node<T> tNode = new Node<T>(value);
        //若为空链表,新建头结点
        if(head==null)
        {
            head=tNode;
        }
        else//(tail!=null)的情况
        {
            tail.next=tNode;
        }
        tail=tNode;
    }
    //出队操作,出队要返回队列头节点,先处理核心的问题,然后再去管特殊情况
    public T dequeue() throws Exception {
        if(head==null)
        {
            System.out.println("empty Queue");
            throw new Exception("empty");
        }
        if(head.next==null)
        {
            tail=null;
        }
       T value=head.value;
       head=head.next;
       return value;
    }
    // 检查队列是否为空
    public boolean isEmpty() {
        return head == null;
    }
    // 获取队列头部元素
    public T peek() {
        if (head == null) {
            throw new IllegalStateException("Queue is empty");
        }
        return head.value;
    }
    // 测试用例
    public static void main(String[] args) throws Exception {
        LinkedQueue<Integer> queue = new LinkedQueue<>();
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        System.out.println("队列头部元素: " + queue.peek());
        System.out.println("出队元素: " + queue.dequeue());
        System.out.println("队列头部元素: " + queue.peek());
        System.out.println("出队元素: " + queue.dequeue());
        System.out.println("队列头部元素: " + queue.peek());
        System.out.println("出队元素: " + queue.dequeue());

        System.out.println("队列是否为空: " + queue.isEmpty());
    }
}```


#### **双向链表实现双端队列**

```java
package class4;/*
 *项目名: lanqiao_doexercise
 *文件名:DoublyLinkedDeque
 *创建者:ZEL
 *创建时间:2024/8/3 21:38
 *描述:TODO
 */

class DoublyLinkedDeque<T> {
    // 定义链表节点
    private static class Node<T> {
        private T value;
        private Node<T> prev;
        private Node<T> next;

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

    private Node<T> head; // 队列头
    private Node<T> tail; // 队列尾

    public DoublyLinkedDeque() {
        head = null;
        tail = null;
    }

    // 从队列头部插入元素
    public void addFirst(T value) {
        Node<T> newNode = new Node<>(value);
        if (head == null) {
            head = newNode;
            tail = newNode;
        } else {
            newNode.next = head;
            head.prev = newNode;
            head = newNode;
        }
    }

    // 从队列尾部插入元素
    public void addLast(T value) {
        Node<T> newNode = new Node<>(value);
        if (tail == null) {
            head = newNode;
            tail = newNode;
        } else {
            newNode.prev = tail;
            tail.next = newNode;
            tail = newNode;
        }
    }

    // 从队列头部删除元素
    public T removeFirst() {
        if (head == null) {
            throw new IllegalStateException("Deque is empty");
        }
        T value = head.value;
        head = head.next;
        if (head == null) {
            tail = null;
        } else {
            head.prev = null;
        }
        return value;
    }

    // 从队列尾部删除元素
    public T removeLast() {
        if (tail == null) {
            throw new IllegalStateException("Deque is empty");
        }
        T value = tail.value;
        tail = tail.prev;
        if (tail == null) {
            head = null;
        } else {
            tail.next = null;
        }
        return value;
    }

    // 检查队列是否为空
    public boolean isEmpty() {
        return head == null;
    }

    // 测试用例
    public static void main(String[] args) {
        DoublyLinkedDeque<Integer> deque = new DoublyLinkedDeque<>();
        deque.addFirst(1);
        deque.addLast(2);
        deque.addFirst(3);
        deque.addLast(4);

        System.out.println("从队列头部删除: " + deque.removeFirst()); // 3
        System.out.println("从队列尾部删除: " + deque.removeLast()); // 4
        System.out.println("从队列头部删除: " + deque.removeFirst()); // 1
        System.out.println("从队列尾部删除: " + deque.removeLast()); // 2

        System.out.println("队列是否为空: " + deque.isEmpty()); // true
    }
}


1.2.2数组实现队列

ac6:数组实现队列思路: 感觉是一种循环队列的感觉,begin end size

push end++ size++ pop begin++ size--;v57.22 其中处理可以使用取余来处理好像比价简单。

循环的东西一定要想着%

package class4;/*
 *项目名: lanqiao_doexercise
 *文件名:CircularQueue_T
 *创建者:ZEL
 *创建时间:2024/8/4 15:02
 *描述:TODO
 */

public class CircularQueue_T {
    public static class ArrayQueue<T> {
        private T[] arr;
        private int polli;
        private int pushi;
        private int size;
        private int limit;
        public ArrayQueue(int num)//请补充T的时候如何操作
        {
            //如果使用泛型T
            arr=(T[])new Object[num];
            polli =0;
            pushi =0;
            size=0;
            limit=num;
        }
        public void push(T value) throws Exception {
            if(size==limit)
            {
                throw new Exception("队列满了");
            }
            size++;
            arr[pushi]=value;
            pushi=(pushi+1)%limit;
        }
        public T pop() throws Exception {
            if(size==0)
            {
                throw new Exception("队列为空");
            }
            T ans=arr[polli];
            arr[polli]=null;
            size--;
            polli = (polli+1)%limit;
            return ans;
        }
        public boolean isEmpty()
        {
            return size==0;
        }
    }

    public static void main(String[] args) {
        try {
            ArrayQueue<Integer> queue = new ArrayQueue<>(5);

            queue.push(1);
            queue.push(2);
            queue.push(3);
            queue.push(4);
            queue.push(5);

            System.out.println(queue.pop()); // 应该输出1
            System.out.println(queue.pop()); // 应该输出2

            queue.push(6);
            queue.push(7);

            while (!queue.isEmpty()) {
                System.out.println(queue.pop()); // 应该依次输出3, 4, 5, 6, 7
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.3栈

1.3.1 链表实现栈

单向链表实现栈

比较简单核心在于:链表头永远是栈顶元素

package class4;/*
 *项目名: lanqiao_doexercise
 *文件名:LinkedStack
 *创建者:ZEL
 *创建时间:2024/8/3 21:32
 *描述:TODO
 */

class LinkedStack<T> {
    // 定义链表节点
    private static class Node<T> {
        private T value;
        private Node<T> next;

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

    private Node<T> top; // 栈顶

    public LinkedStack() {
        top = null;
    }

    // 入栈操作
    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        newNode.next = top;
        top = newNode;
    }

    // 出栈操作
    public T pop() {
        if (top == null) {
            throw new IllegalStateException("Stack is empty");
        }
        T value = top.value;
        top = top.next;
        return value;
    }

    // 检查栈是否为空
    public boolean isEmpty() {
        return top == null;
    }

    // 获取栈顶元素
    public T peek() {
        if (top == null) {
            throw new IllegalStateException("Stack is empty");
        }
        return top.value;
    }

    // 测试用例
    public static void main(String[] args) {
        LinkedStack<Integer> stack = new LinkedStack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);

        System.out.println("栈顶元素: " + stack.peek());
        System.out.println("出栈元素: " + stack.pop());
        System.out.println("栈顶元素: " + stack.peek());
        System.out.println("出栈元素: " + stack.pop());
        System.out.println("栈顶元素: " + stack.peek());
        System.out.println("出栈元素: " + stack.pop());

        System.out.println("栈是否为空: " + stack.isEmpty());
    }
}

1.3.2数组实现栈思路:

ac5:数组实现栈思路:数组+index控制,index为要放入元素的位置 放入index++再放入 index--删除??。

比队列简单

package class4;/*
 *项目名: lanqiao_doexercise
 *文件名:ArrayStack
 *创建者:ZEL
 *创建时间:2024/8/4 15:13
 *描述:TODO:利用数组实现栈,没有使用size主要top就包括很多东西了
 */

public class ArrayStack<T> {
    private T[] arr;
    private int top;
    private int limit;

    public ArrayStack(int size) {
        arr = (T[]) new Object[size];
        top = -1;
        limit = size;
    }

    // 压入栈
    public void push(T value) throws Exception {
        if (top == limit - 1) {
            throw new Exception("栈满了");
        }
        arr[++top] = value;//先++再赋值。
    }

    // 弹出栈
    public T pop() throws Exception {
        if (top == -1) {
            throw new Exception("栈为空");
        }
        T value = arr[top];
        arr[top--] = null; // 方便垃圾回收
        return value;
    }

    // 查看栈顶元素
    public T peek() throws Exception {
        if (top == -1) {
            throw new Exception("栈为空");
        }
        return arr[top];
    }

    // 检查栈是否为空
    public boolean isEmpty() {
        return top == -1;
    }

    // 测试用例
    public static void main(String[] args) {
        try {
            ArrayStack<Integer> stack = new ArrayStack<>(5);

            stack.push(1);
            stack.push(2);
            stack.push(3);
            stack.push(4);
            stack.push(5);

            System.out.println(stack.pop()); // 应该输出5
            System.out.println(stack.pop()); // 应该输出4

            stack.push(6);
            stack.push(7);

            while (!stack.isEmpty()) {
                System.out.println(stack.pop()); // 应该依次输出7, 6, 3, 2, 1
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


2.链表的运用

2.1题目

2.1.1单链表和双链表如何反转

ac1:写这个代码,提示:pre代表前一个节点,next代表next节点所指的下个节点

public static Node reverseLinkedList(Node head) v19.46

    //反转单链表(给出一个链表的头结点,反转之后返回反转之后的头结点)
    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 reverseDoubleLinkedList(DoubleNode head)
    {
        DoubleNode tmp =null;
        DoubleNode pre=null;
        while(head!=null)
        {
            tmp=head.next;
            head.next=head.last;
            head.last=tmp;
            pre=head;
            head=tmp;
        }
        return pre;
    }

2.1.2把给定的值都删除

ac2:提示:头部可能被删除,所以要返回新头部Node,要考虑一些边界条件很麻烦。

public static Node removeValue(Node node,int num) v33.47

 //把给定的值都删除(自己)
    public static Node removeValue(Node node,int num)
    {
        if(node==null)
        {
            System.out.println("空链表");
            return null;
        }
        while(node.value==num)
        {
            node=node.next;
        }
        Node pre=null;
        Node cur=node;

        while(cur!=null)
        {
            if(cur.value==num)
            {
                pre.next=cur.next;
            }else {
                pre = cur;
            }
            cur=cur.next;
        }
        return node;
    }

2.2经典面试题

2.2.1返回栈中最小元素的功能

实现一个特殊的栈,在基本功能基础上,再实现返回一个栈中最小元素的功能。

pop、push、getMin操作的时间复杂度都是O(1)。 设计栈类型可以使用现成的栈结构。

ac7:提示:增加一个最小栈的功能,push过程当前值大于栈顶,重复压入栈顶,小于栈顶的时候push当前值。

自己做的收获:实现的过程中一定要在pop、peek、top过程中注意判断栈是否为空

lc155

import java.util.Stack;
//https://leetcode.cn/problems/min-stack/
class MinStack {
    private Stack<Integer> stack;    // 主栈,存储所有元素
    private Stack<Integer> minStack; // 辅助栈,存储当前的最小值

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

    public void push(int val) {
        stack.push(val);
        // 辅助栈为空或者当前值小于等于辅助栈的栈顶值时,将当前值推入辅助栈
        if (minStack.isEmpty() || val <= minStack.peek()) {
            minStack.push(val);
        } else {
            // 否则,将辅助栈的栈顶值重复推入辅助栈
            minStack.push(minStack.peek());
        }
    }

    public void pop() {
        // 当主栈和辅助栈都不为空时,弹出主栈和辅助栈的栈顶值
        if (!stack.isEmpty() && !minStack.isEmpty()) {
            stack.pop();
            minStack.pop();
        } else {
            throw new RuntimeException("Stack is empty");
        }
    }

    public int top() {
        if (!stack.isEmpty()) {
            return stack.peek();
        }
        throw new RuntimeException("Stack is empty");
    }

    public int getMin() {
        if (!minStack.isEmpty()) {
            return minStack.peek();
        }
        throw new RuntimeException("Min stack is empty");
    }
}

2.2.2栈构造队列

ac8:如何使用栈实现队列(拼出队列)

提示:创建两个栈 push栈与pop栈 原则 1倒数据的时候要一次性倒完。2pop栈为空 push栈才能倒数据进入pop栈。v1.21.10

根据思路纯自己写出来 lc232

/*
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

    void push(int x) 将元素 x 推到队列的末尾
    int pop() 从队列的开头移除并返回元素
    int peek() 返回队列开头的元素
    boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

    你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
    你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
*/

class MyQueue {
    Stack <Integer>pushStack;
    Stack <Integer>popStack;
    public MyQueue() {
        pushStack=new Stack<>();
        popStack=new Stack<>();
    }
    
    public void push(int x) {
        pushStack.push(x);
    }
    
    public int pop() {
        if(popStack.empty())
        {
            fromStackTo();
        }
        return popStack.pop();
    }
    
    public int peek() {
        if(popStack.empty())
        {
            fromStackTo();
        }
        return popStack.peek();
    }
    
    public boolean empty() {
        return(pushStack.isEmpty()&&popStack.isEmpty());
    }

    public void fromStackTo() {
        while(!pushStack.isEmpty())
        {
            popStack.push(pushStack.pop());
        }
    }
}


2.2.3队列构造栈

ac9:如何使用队列实现栈

提示:两个队列来回使用,每次剩余最后一个数给用户。(效果比较差)v1.28.58 lc255

//队列本质:Queue queue =new LinkedList();来定义

入队操作使用offer 出队操作使用poll 可以使用peek查看队列对头元素。

/*

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

    void push(int x) 将元素 x 压入栈顶。
    int pop() 移除并返回栈顶元素。
    int top() 返回栈顶元素。
    boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

 

注意:

    你只能使用队列的标准操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
    你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。


*/

class MyStack {
    Queue<Integer> queue1;
    Queue<Integer> queue2;
    public MyStack() {
        queue1=new LinkedList<>();
        queue2=new LinkedList<>();
    }
    
    public void push(int x) {
        queue1.offer(x);
    }
    
    public int pop() {
        if(queue1.isEmpty())
        {
            return 0;
        }
        while(queue1.size()>1)
        {
            queue2.offer(queue1.poll());
        }
        int ans1=queue1.poll();
        Queue<Integer> queue=new LinkedList<>();
        queue=queue1;
        queue1=queue2;
        queue2=queue;
        return ans1;
    }
    
    public int top() {
        while(queue1.size()>1)
        {
            queue2.offer(queue1.poll());
        }
        int ans=queue1.peek();
        queue2.offer(queue1.poll());
        Queue<Integer> queue=new LinkedList<>();
        queue=queue1;
        queue1=queue2;
        queue2=queue;
        return ans;
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}


4.递归

(大问题拆分为小问题)

  • ac10:求一个数组中L到R的最大值 (使用递归来做,我觉得边界问题要麻烦一点。)v1.35.27

    分析递归的时候尽量画递归的调用图。

  • Master公式:分析递归方法的时间复杂度。

    计算递归函数的复杂度,主要要将其写为T(N)=a*T(N/b)+O(N/d)然后比较log ba 与d之间的大小关系决定。

5.哈希表

ps:内部数据量不管多大,它的各种操作复杂度都是O(1)

  • 非基础类型的key放入hashmap中占用8字节。
  • hashmap原生的一些数据类型都是按值传递。Integer等等
  • 非原生类型按引用传递。自定义的数据类型

image-20240804223841295

posted @ 2024-08-04 22:41  七七喜欢你  阅读(14)  评论(0)    收藏  举报