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
入队操作使用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等等
- 非原生类型按引用传递。自定义的数据类型
本文来自博客园,作者:七七喜欢你,转载请注明原文链接:https://www.cnblogs.com/EternalX/articles/18342358
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型