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 @   七七喜欢你  阅读(2)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型
点击右上角即可分享
微信分享提示