1实现一个栈的基本操作(入栈、出栈、取栈顶元素、判断是否为空)。
在Java中,可以使用数组或链表来实现栈的基本操作。下面是一个使用数组实现的简单栈类:
public class Stack { private int[] stackArray; private int top; private int maxSize; // 构造函数,初始化栈的大小 public Stack(int size) { maxSize = size; stackArray = new int[maxSize]; top = -1; // 栈顶指针初始为-1,表示栈为空 } // 判断栈是否为空 public boolean isEmpty() { return (top == -1); } // 判断栈是否已满 public boolean isFull() { return (top == maxSize - 1); } // 入栈操作 public void push(int value) { if (isFull()) { System.out.println("Stack is full. Cannot push " + value); return; } stackArray[++top] = value; } // 出栈操作 public int pop() { if (isEmpty()) { System.out.println("Stack is empty. Cannot pop"); return -1; // 返回一个特殊值表示栈为空 } return stackArray[top--]; } // 取栈顶元素 public int peek() { if (isEmpty()) { System.out.println("Stack is empty. Cannot peek"); return -1; // 返回一个特殊值表示栈为空 } return stackArray[top]; } // 测试栈的基本操作 public static void main(String[] args) { Stack stack = new Stack(5); // 创建一个大小为5的栈 stack.push(10); stack.push(20); stack.push(30); System.out.println("Top element is: " + stack.peek()); // 输出栈顶元素 System.out.println("Stack is empty: " + stack.isEmpty()); // 判断栈是否为空 System.out.println("Popped element: " + stack.pop()); // 出栈并输出出栈的元素 System.out.println("Popped element: " + stack.pop()); // 出栈并输出出栈的元素 System.out.println("Popped element: " + stack.pop()); // 出栈并输出出栈的元素 System.out.println("Stack is empty: " + stack.isEmpty()); // 判断栈是否为空 } }
Stack: 定义了一个栈类,包含一个整数数组stackArray来存储栈中的元素,一个整数top作为栈顶指针,以及一个整数maxSize表示栈的最大容量。isEmpty: 检查栈是否为空,如果top等于 -1,则栈为空。isFull: 检查栈是否已满,如果top等于maxSize - 1,则栈已满。push: 将元素添加到栈顶(数组的末尾),如果栈已满,打印错误信息。pop: 移除并返回栈顶元素,如果栈为空,打印错误信息并返回 -1。peek: 返回栈顶元素但不移除它,如果栈为空,打印错误信息并返回 -1。main: 测试栈的基本操作,包括入栈、出栈、取栈顶元素和判断是否为空。
2使用两个栈实现一个队列的操作。
使用两个栈实现一个队列的操作,可以通过以下步骤来实现:
- 使用两个栈
stack1和stack2。 - 入队操作时,将元素压入
stack1。 - 出队操作时,如果
stack2为空,则将stack1中的所有元素依次弹出并压入stack2,然后从stack2弹出栈顶元素;如果stack2不为空,直接从stack2弹出栈顶元素。
下面是使用Java语言实现的代码:
import java.util.Stack; public class QueueUsingStacks { private Stack<Integer> stack1; private Stack<Integer> stack2; // 构造函数初始化两个栈 public QueueUsingStacks() { stack1 = new Stack<>(); stack2 = new Stack<>(); } // 入队操作 public void enqueue(int value) { stack1.push(value); } // 出队操作 public int dequeue() { if (stack2.isEmpty()) { while (!stack1.isEmpty()) { stack2.push(stack1.pop()); } } if (stack2.isEmpty()) { throw new RuntimeException("Queue is empty"); } return stack2.pop(); } // 判断队列是否为空 public boolean isEmpty() { return stack1.isEmpty() && stack2.isEmpty(); } // 获取队列的大小 public int size() { return stack1.size() + stack2.size(); } public static void main(String[] args) { QueueUsingStacks queue = new QueueUsingStacks(); queue.enqueue(1); queue.enqueue(2); queue.enqueue(3); System.out.println("Dequeued: " + queue.dequeue()); // 输出 1 System.out.println("Dequeued: " + queue.dequeue()); // 输出 2 queue.enqueue(4); System.out.println("Dequeued: " + queue.dequeue()); // 输出 3 System.out.println("Dequeued: " + queue.dequeue()); // 输出 4 System.out.println("Is queue empty? " + queue.isEmpty()); // 输出 true } }
QueueUsingStacks: 定义了一个使用两个栈实现的队列类。enqueue: 将元素压入stack1。dequeue: 如果stack2为空,则将stack1中的所有元素依次弹出并压入stack2,然后从stack2弹出栈顶元素;如果stack2不为空,直接从stack2弹出栈顶元素。isEmpty: 判断队列是否为空,即两个栈都为空。size: 返回队列的大小,即两个栈的元素总数。main: 测试队列的基本操作,包括入队、出队、判断是否为空等。
3使用一个栈实现另一个栈的反转。
使用一个栈实现另一个栈的反转,可以通过以下步骤来实现:
- 创建一个辅助栈
auxStack。 - 将原栈
stack中的所有元素依次弹出并压入auxStack。 - 将
auxStack中的元素再次弹出并压入stack,这样stack中的元素顺序就被反转了。
下面是使用Java语言实现的代码:
import java.util.Stack; public class StackReversal { // 方法:使用一个栈反转另一个栈 public static void reverseStack(Stack<Integer> stack) { Stack<Integer> auxStack = new Stack<>(); // 将原栈中的所有元素弹出并压入辅助栈 while (!stack.isEmpty()) { auxStack.push(stack.pop()); } // 将辅助栈中的所有元素弹出并压回原栈,完成反转 while (!auxStack.isEmpty()) { stack.push(auxStack.pop()); } } public static void main(String[] args) { Stack<Integer> stack = new Stack<>(); stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); System.out.println("Original stack: " + stack); reverseStack(stack); System.out.println("Reversed stack: " + stack); } }
reverseStack: 定义了一个方法,用于反转给定的栈。- 创建一个辅助栈
auxStack。 - 将原栈
stack中的所有元素依次弹出并压入auxStack。 - 将
auxStack中的所有元素再次弹出并压入stack,这样stack中的元素顺序就被反转了。
- 创建一个辅助栈
main: 测试栈的反转操作。- 创建一个栈
stack并压入一些元素。 - 打印原始栈的内容。
- 调用
reverseStack方法反转栈。 - 打印反转后的栈的内容。
- 创建一个栈
4判断一个字符串是否为回文(可以使用栈来实现)。
判断一个字符串是否为回文,可以使用栈来实现。具体步骤如下:
- 创建一个栈。
- 将字符串的前半部分字符依次压入栈中。
- 如果字符串长度是奇数,跳过中间的字符。
- 从栈顶开始弹出字符,并与字符串的后半部分进行比较。
- 如果所有字符都匹配,则该字符串是回文;否则不是。
下面是使用Java语言实现的代码:
import java.util.Stack; public class PalindromeChecker { // 方法:判断一个字符串是否为回文 public static boolean isPalindrome(String str) { Stack<Character> stack = new Stack<>(); int length = str.length(); int mid = length / 2; // 将前半部分字符压入栈中 for (int i = 0; i < mid; i++) { stack.push(str.charAt(i)); } // 如果字符串长度是奇数,跳过中间的字符 if (length % 2 != 0) { mid++; } // 从栈顶开始弹出字符,并与后半部分进行比较 for (int i = mid; i < length; i++) { if (stack.isEmpty() || stack.pop() != str.charAt(i)) { return false; } } return true; } public static void main(String[] args) { String str1 = "racecar"; String str2 = "hello"; String str3 = "madam"; String str4 = "abba"; System.out.println(str1 + " is palindrome: " + isPalindrome(str1)); // 输出 true System.out.println(str2 + " is palindrome: " + isPalindrome(str2)); // 输出 false System.out.println(str3 + " is palindrome: " + isPalindrome(str3)); // 输出 true System.out.println(str4 + " is palindrome: " + isPalindrome(str4)); // 输出 true } }
isPalindrome: 定义了一个方法,用于判断给定的字符串是否为回文。- 创建一个栈
stack。 - 计算字符串的长度
length和中间位置mid。 - 将字符串的前半部分字符依次压入栈中。
- 如果字符串长度是奇数,跳过中间的字符。
- 从栈顶开始弹出字符,并与字符串的后半部分进行比较。如果所有字符都匹配,则返回
true,否则返回false。
- 创建一个栈
main: 测试几个字符串是否为回文。- 创建几个示例字符串并调用
isPalindrome方法进行判断。 - 打印结果。
- 创建几个示例字符串并调用
5实现一个队列的基本操作(入队、出队、取队首元素、判断是否为空)。
好的,下面是使用Java实现一个队列的基本操作(入队、出队、取队首元素、判断是否为空)的代码。我们将使用Java的
LinkedList类来实现这个队列。import java.util.LinkedList; import java.util.NoSuchElementException; public class Queue<T> { private LinkedList<T> list = new LinkedList<>(); // 判断队列是否为空 public boolean isEmpty() { return list.isEmpty(); } // 入队操作 public void enqueue(T item) { list.addLast(item); } // 出队操作 public T dequeue() { if (isEmpty()) { throw new NoSuchElementException("Dequeue from empty queue"); } return list.removeFirst(); } // 取队首元素 public T front() { if (isEmpty()) { throw new NoSuchElementException("Front from empty queue"); } return list.getFirst(); } // 测试代码 public static void main(String[] args) { Queue<Integer> queue = new Queue<>(); System.out.println("队列是否为空? " + queue.isEmpty()); // True queue.enqueue(1); queue.enqueue(2); queue.enqueue(3); System.out.println("队列是否为空? " + queue.isEmpty()); // False System.out.println("队首元素: " + queue.front()); // 1 System.out.println("出队元素: " + queue.dequeue()); // 1 System.out.println("出队元素: " + queue.dequeue()); // 2 System.out.println("队首元素: " + queue.front()); // 3 System.out.println("出队元素: " + queue.dequeue()); // 3 System.out.println("队列是否为空? " + queue.isEmpty()); // True } }
isEmpty: 检查队列是否为空。如果队列为空,返回true,否则返回false。enqueue: 将一个元素添加到队列的末尾。dequeue: 从队列的头部移除并返回一个元素。如果队列为空,抛出NoSuchElementException异常。front: 返回队列的头部元素而不移除它。如果队列为空,抛出NoSuchElementException异常。main: 测试代码,演示如何使用上述方法进行队列操作。
通过这些基本操作,你可以实现一个功能齐全的队列。
6使用两个队列实现一个栈的操作。
好的,使用两个队列来实现一个栈的操作是一个经典的面试题。我们可以利用两个队列来模拟栈的后进先出(LIFO)特性。
下面是使用Java实现这个功能的代码:
import java.util.LinkedList; import java.util.Queue; public class StackUsingTwoQueues<T> { private Queue<T> queue1; private Queue<T> queue2; public StackUsingTwoQueues() { queue1 = new LinkedList<>(); queue2 = new LinkedList<>(); } // 入栈操作 public void push(T item) { queue1.add(item); } // 出栈操作 public T pop() { if (queue1.isEmpty()) { throw new IllegalStateException("Pop from empty stack"); } // 将queue1中的元素转移到queue2,直到只剩下一个元素 while (queue1.size() > 1) { queue2.add(queue1.remove()); } // 最后一个元素就是我们要弹出的元素 T topElement = queue1.remove(); // 交换queue1和queue2 Queue<T> temp = queue1; queue1 = queue2; queue2 = temp; return topElement; } // 取栈顶元素 public T top() { if (queue1.isEmpty()) { throw new IllegalStateException("Top from empty stack"); } // 将queue1中的元素转移到queue2,直到只剩下一个元素 while (queue1.size() > 1) { queue2.add(queue1.remove()); } // 最后一个元素就是我们要返回的栈顶元素 T topElement = queue1.peek(); // 将最后一个元素再放回queue2,并交换queue1和queue2 queue2.add(queue1.remove()); Queue<T> temp = queue1; queue1 = queue2; queue2 = temp; return topElement; } // 判断栈是否为空 public boolean isEmpty() { return queue1.isEmpty(); } // 测试代码 public static void main(String[] args) { StackUsingTwoQueues<Integer> stack = new StackUsingTwoQueues<>(); stack.push(1); stack.push(2); stack.push(3); System.out.println("栈顶元素:" + stack.top()); // 3 System.out.println("出栈元素:" + stack.pop()); // 3 System.out.println("栈顶元素:" + stack.top()); // 2 System.out.println("出栈元素:" + stack.pop()); // 2 System.out.println("栈顶元素:" + stack.top()); // 1 System.out.println("出栈元素:" + stack.pop()); // 1 System.out.println("栈是否为空? " + stack.isEmpty()); // True } }
push: 将元素添加到queue1中。由于queue1是先进先出的队列,新添加的元素会在最后面。pop: 为了模拟栈的后进先出特性,我们需要将queue1中的元素逐个移到queue2,直到只剩下一个元素。这个剩下的元素就是栈顶元素,将其移除并返回。然后交换queue1和queue2,使得queue1再次成为主队列。top: 类似于pop,但不同的是我们不移除最后一个元素,只是返回它。同样需要将queue1中的元素逐个移到queue2,直到只剩下一个元素。然后将这个元素再放回queue2,并交换queue1和queue2。isEmpty: 检查queue1是否为空。如果为空,则表示栈为空。main: 测试代码,演示如何使用上述方法进行栈操作。
通过这些基本操作,你可以使用两个队列来实现一个功能齐全的栈。
7使用队列实现一个浏览器的后退功能。
好的,使用队列实现一个浏览器的后退功能是一个经典的面试题。我们可以利用两个队列来实现这个功能:一个用于存储当前页面和前进页面,另一个用于存储后退页面。
下面是使用Java实现这个功能的代码:
import java.util.LinkedList; import java.util.Queue; public class BrowserHistory { private Queue<String> backStack; private Queue<String> forwardStack; private String currentPage; public BrowserHistory() { backStack = new LinkedList<>(); forwardStack = new LinkedList<>(); currentPage = null; } // 访问新页面 public void visit(String url) { if (currentPage != null) { backStack.add(currentPage); } currentPage = url; forwardStack.clear(); // 清空前进栈 System.out.println("Visiting: " + url); } // 后退操作 public void back() { if (!backStack.isEmpty()) { forwardStack.add(currentPage); currentPage = backStack.remove(); System.out.println("Back to: " + currentPage); } else { System.out.println("No pages to go back to."); } } // 前进操作 public void forward() { if (!forwardStack.isEmpty()) { backStack.add(currentPage); currentPage = forwardStack.remove(); System.out.println("Forward to: " + currentPage); } else { System.out.println("No pages to go forward to."); } } // 获取当前页面 public String getCurrentPage() { return currentPage; } // 测试代码 public static void main(String[] args) { BrowserHistory browserHistory = new BrowserHistory(); browserHistory.visit("google.com"); browserHistory.visit("youtube.com"); browserHistory.visit("facebook.com"); browserHistory.back(); // Back to: youtube.com browserHistory.back(); // Back to: google.com browserHistory.forward(); // Forward to: youtube.com browserHistory.visit("twitter.com"); // Visiting: twitter.com browserHistory.back(); // Back to: youtube.com browserHistory.back(); // Back to: google.com browserHistory.back(); // No pages to go back to. browserHistory.forward(); // Forward to: youtube.com browserHistory.forward(); // Forward to: twitter.com browserHistory.forward(); // No pages to go forward to. } }
visit: 访问一个新页面时,将当前页面添加到backStack中,并清空forwardStack。然后更新currentPage为新页面。back: 执行后退操作时,如果backStack不为空,则将当前页面添加到forwardStack中,并将currentPage更新为backStack中的最后一个元素。否则,输出没有页面可以后退。forward: 执行前进操作时,如果forwardStack不为空,则将当前页面添加到backStack中,并将currentPage更新为forwardStack中的最后一个元素。否则,输出没有页面可以前进。getCurrentPage: 返回当前页面。main: 测试代码,演示如何使用上述方法进行浏览器的后退和前进操作。
通过这些基本操作,你可以使用队列来实现一个功能齐全的浏览器后退功能。
8判断一个队列是否有效(即队列中的元素是否按顺序排列)。
要判断一个队列中的元素是否按顺序排列,我们可以使用Java语言来实现。假设我们有一个整数队列,并且我们希望检查这个队列中的元素是否按升序排列。
以下是一个简单的Java程序示例,用于判断队列中的元素是否按顺序排列:
import java.util.LinkedList; import java.util.Queue; public class QueueOrderChecker { public static void main(String[] args) { // 创建一个队列并添加一些元素 Queue<Integer> queue = new LinkedList<>(); queue.add(1); queue.add(2); queue.add(3); queue.add(4); queue.add(5); // 检查队列是否按顺序排列 boolean isOrdered = isQueueOrdered(queue); System.out.println("队列是否按顺序排列: " + isOrdered); } /** * 检查队列中的元素是否按顺序排列 * @param queue 要检查的队列 * @return 如果队列按顺序排列则返回true,否则返回false */ public static boolean isQueueOrdered(Queue<Integer> queue) { if (queue == null || queue.size() <= 1) { return true; // 空队列或只有一个元素的队列被认为是有序的 } int previous = queue.poll(); // 取出第一个元素作为初始比较值 while (!queue.isEmpty()) { int current = queue.poll(); if (current < previous) { return false; // 如果当前元素小于前一个元素,则队列不是按顺序排列的 } previous = current; // 更新前一个元素为当前元素 } return true; // 所有元素都按顺序排列 } }
在这个程序中,我们定义了一个方法
isQueueOrdered,它接受一个 Queue<Integer> 类型的参数,并返回一个布尔值,表示队列中的元素是否按顺序排列。具体步骤如下:
- 检查队列是否为空或只有一个元素。如果是,则直接返回
true,因为空队列或只有一个元素的队列被认为是有序的。 - 使用
poll方法从队列中取出第一个元素作为初始比较值。 - 遍历队列中的剩余元素,逐个与前一个元素进行比较。如果发现任何一个元素小于前一个元素,则返回
false。 - 如果遍历完所有元素都没有发现任何不符合条件的情况,则返回
true。
通过这种方式,我们可以有效地判断一个队列中的元素是否按顺序排列。
9实现一个循环队列。
好的,下面是一个使用Java语言实现的循环队列(Circular Queue)的示例。这个实现包括基本的入队、出队、检查是否为空、检查是否已满以及打印队列内容的方法。
public class CircularQueue { private int[] queue; private int front; private int rear; private int size; private int capacity; // 构造函数,初始化队列 public CircularQueue(int capacity) { this.capacity = capacity; queue = new int[capacity]; front = -1; rear = -1; size = 0; } // 检查队列是否为空 public boolean isEmpty() { return size == 0; } // 检查队列是否已满 public boolean isFull() { return size == capacity; } // 入队操作 public void enqueue(int element) { if (isFull()) { System.out.println("Queue is full"); return; } if (front == -1) { // 第一次插入元素 front = 0; } rear = (rear + 1) % capacity; queue[rear] = element; size++; } // 出队操作 public int dequeue() { if (isEmpty()) { System.out.println("Queue is empty"); return -1; // 返回一个特殊值表示队列为空 } int element = queue[front]; if (front == rear) { // 队列中只有一个元素 front = -1; rear = -1; } else { front = (front + 1) % capacity; } size--; return element; } // 获取队列的前端元素 public int peek() { if (isEmpty()) { System.out.println("Queue is empty"); return -1; // 返回一个特殊值表示队列为空 } return queue[front]; } // 打印队列中的元素 public void printQueue() { if (isEmpty()) { System.out.println("Queue is empty"); return; } System.out.print("Queue elements: "); int count = size; int index = front; while (count > 0) { System.out.print(queue[index] + " "); index = (index + 1) % capacity; count--; } System.out.println(); } public static void main(String[] args) { CircularQueue cq = new CircularQueue(5); cq.enqueue(1); cq.enqueue(2); cq.enqueue(3); cq.enqueue(4); cq.enqueue(5); cq.printQueue(); // 输出: Queue elements: 1 2 3 4 5 cq.dequeue(); cq.printQueue(); // 输出: Queue elements: 2 3 4 5 cq.enqueue(6); cq.printQueue(); // 输出: Queue elements: 2 3 4 5 6 cq.enqueue(7); // 队列已满,无法添加更多元素 cq.printQueue(); // 输出: Queue elements: 2 3 4 5 6 } }
- 属性:
queue:用于存储队列元素的数组。front:指向队列头部的索引。rear:指向队列尾部的索引。size:当前队列中的元素数量。capacity:队列的最大容量。
- 构造函数:初始化队列的属性。
- 方法:
isEmpty():检查队列是否为空。isFull():检查队列是否已满。enqueue(int element):将元素添加到队列尾部。如果队列已满,则提示“Queue is full”。dequeue():从队列头部移除并返回元素。如果队列为空,则提示“Queue is empty”并返回特殊值-1。peek():返回队列头部的元素而不移除它。如果队列为空,则提示“Queue is empty”并返回特殊值-1。printQueue():打印队列中的所有元素。如果队列为空,则提示“Queue is empty”。
- 主方法:测试循环队列的基本操作。
10使用栈或队列解决括号匹配问题。
括号匹配问题是一个经典的编程问题,通常使用栈来解决。这是因为栈的后进先出(LIFO)特性非常适合处理成对的符号匹配问题。
下面是一个使用Java语言实现的示例代码,用于检查括号是否匹配:
import java.util.Stack; public class BracketMatcher { // 方法来检查括号是否匹配 public static boolean isValid(String s) { // 创建一个栈来存储左括号 Stack<Character> stack = new Stack<>(); // 遍历字符串中的每个字符 for (char c : s.toCharArray()) { // 如果是左括号,压入栈中 if (c == '(' || c == '{' || c == '[') { stack.push(c); } else { // 如果是右括号,检查栈是否为空以及栈顶元素是否匹配 if (stack.isEmpty()) { return false; // 栈为空,说明没有对应的左括号 } char top = stack.pop(); if ((c == ')' && top != '(') || (c == '}' && top != '{') || (c == ']' && top != '[')) { return false; // 不匹配的情况 } } } // 如果栈为空,说明所有的括号都匹配了 return stack.isEmpty(); } public static void main(String[] args) { // 测试用例 String test1 = "()"; String test2 = "({[()]})"; String test3 = "([)]"; String test4 = "((("; String test5 = "{[()]}"; System.out.println(test1 + " -> " + isValid(test1)); // true System.out.println(test2 + " -> " + isValid(test2)); // true System.out.println(test3 + " -> " + isValid(test3)); // false System.out.println(test4 + " -> " + isValid(test4)); // false System.out.println(test5 + " -> " + isValid(test5)); // true } }
- 栈的使用:我们使用一个栈来存储遇到的左括号。当遇到右括号时,检查栈顶元素是否是对应的左括号。如果是,则弹出栈顶元素;如果不是,或者栈为空,则说明括号不匹配。
- 遍历字符串:我们遍历输入字符串的每一个字符。如果字符是左括号,就将其压入栈中;如果字符是右括号,就检查栈顶元素并进行处理。
- 最终检查:遍历结束后,如果栈为空,说明所有的括号都匹配了;否则,说明有未匹配的左括号。
通过这种方式,我们可以有效地解决括号匹配的问题。
11使用栈解决“最近较小的数”问题。
“最近较小的数”问题是一个经典的编程问题,通常使用栈来解决。这个问题的目标是找到数组中每个元素左边第一个比它小的元素。如果没有这样的元素,则返回 -1。
下面是一个使用Java语言实现的示例代码:
import java.util.Stack; public class NearestSmallerToLeft { // 方法来找到每个元素左边第一个比它小的元素 public static int[] findNearestSmallerToLeft(int[] nums) { int n = nums.length; int[] result = new int[n]; Stack<Integer> stack = new Stack<>(); for (int i = 0; i < n; i++) { // 弹出栈顶元素直到找到一个比当前元素小的元素或者栈为空 while (!stack.isEmpty() && stack.peek() >= nums[i]) { stack.pop(); } // 如果栈为空,说明没有比当前元素小的元素 if (stack.isEmpty()) { result[i] = -1; } else { // 否则,栈顶元素就是最近的较小元素 result[i] = stack.peek(); } // 将当前元素压入栈中 stack.push(nums[i]); } return result; } public static void main(String[] args) { // 测试用例 int[] testArray = {4, 5, 2, 10, 8}; int[] result = findNearestSmallerToLeft(testArray); // 打印结果 System.out.print("Input: "); for (int num : testArray) { System.out.print(num + " "); } System.out.println(); System.out.print("Output: "); for (int res : result) { System.out.print(res + " "); } } }
- 栈的使用:我们使用一个栈来存储遍历过程中遇到的元素。栈中的元素是按从大到小的顺序排列的。
- 遍历数组:我们遍历输入数组的每一个元素。对于每一个元素,我们检查栈顶元素是否大于或等于当前元素。如果是,则弹出栈顶元素,直到找到一个比当前元素小的元素或者栈为空。
- 处理结果:如果栈为空,说明没有比当前元素小的元素,结果数组对应位置设为 -1;否则,栈顶元素就是最近的较小元素,将其存入结果数组。
- 更新栈:将当前元素压入栈中,以便后续元素的比较。
- 输出结果:最后,我们打印输入数组和对应的结果数组。
通过这种方式,我们可以有效地解决“最近较小的数”问题。
12使用队列解决层次遍历二叉树的问题。
层次遍历(也称为广度优先搜索,BFS)是一种遍历二叉树的方式,它按层级从上到下、从左到右访问节点。使用队列是实现层次遍历的常见方法,因为队列的先进先出(FIFO)特性非常适合这种逐层处理的需求。
下面是一个使用Java语言实现的示例代码,用于进行二叉树的层次遍历:
import java.util.LinkedList; import java.util.Queue; // 定义二叉树节点类 class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class LevelOrderTraversal { // 方法来进行层次遍历 public static void levelOrderTraversal(TreeNode root) { if (root == null) { return; } // 创建一个队列来存储节点 Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { // 取出队列头部的节点并打印其值 TreeNode currentNode = queue.poll(); System.out.print(currentNode.val + " "); // 如果左子节点不为空,将其加入队列 if (currentNode.left != null) { queue.add(currentNode.left); } // 如果右子节点不为空,将其加入队列 if (currentNode.right != null) { queue.add(currentNode.right); } } } public static void main(String[] args) { // 构建一个示例二叉树 TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.left.left = new TreeNode(4); root.left.right = new TreeNode(5); root.right.left = new TreeNode(6); root.right.right = new TreeNode(7); // 调用层次遍历方法 System.out.println("Level Order Traversal:"); levelOrderTraversal(root); } }
- TreeNode类:定义了二叉树的节点结构,每个节点包含一个整数值和两个指向左右子节点的引用。
- levelOrderTraversal方法:实现了层次遍历的逻辑。首先检查根节点是否为空,如果为空则直接返回。否则,创建一个队列并将根节点加入队列。然后进入循环,直到队列为空为止。在每次循环中,取出队列头部的节点并打印其值,然后依次将该节点的左子节点和右子节点(如果不为空)加入队列。
- main方法:构建了一个示例二叉树,并调用
levelOrderTraversal方法进行层次遍历。
通过这种方式,我们可以有效地实现二叉树的层次遍历。
13使用栈实现汉诺塔问题的求解。
汉诺塔问题是一个经典的递归问题,通常使用递归方法来解决。然而,我们也可以通过栈来模拟递归过程,从而实现汉诺塔问题的求解。
下面是一个使用Java语言实现的示例代码,通过栈来模拟汉诺塔问题的求解:
import java.util.Stack; public class HanoiTower { // 定义一个类来表示移动操作 static class Move { int n; // 要移动的盘子数量 char from; // 起始柱子 char to; // 目标柱子 char aux; // 辅助柱子 Move(int n, char from, char to, char aux) { this.n = n; this.from = from; this.to = to; this.aux = aux; } } // 方法来求解汉诺塔问题 public static void solveHanoi(int n, char from, char to, char aux) { Stack<Move> stack = new Stack<>(); stack.push(new Move(n, from, to, aux)); while (!stack.isEmpty()) { Move move = stack.pop(); if (move.n == 1) { System.out.println("Move disk 1 from " + move.from + " to " + move.to); } else { stack.push(new Move(move.n - 1, move.aux, move.to, move.from)); stack.push(new Move(1, move.from, move.to, move.aux)); stack.push(new Move(move.n - 1, move.from, move.aux, move.to)); } } } public static void main(String[] args) { int n = 3; // 盘子的数量 System.out.println("Solving Hanoi Tower for " + n + " disks:"); solveHanoi(n, 'A', 'C', 'B'); // A是起始柱子,C是目标柱子,B是辅助柱子 } }
- Move类:定义了一个内部类
Move,用于表示一次移动操作。它包含四个属性:要移动的盘子数量n、起始柱子from、目标柱子to和辅助柱子aux。 - solveHanoi方法:这是求解汉诺塔问题的核心方法。首先创建一个栈,并将初始的移动操作(即从起始柱子到目标柱子)压入栈中。然后进入循环,直到栈为空为止。在每次循环中,弹出栈顶的移动操作。如果该操作只需要移动一个盘子,则直接打印移动步骤;否则,将分解后的子操作依次压入栈中。
- main方法:设置盘子的数量并调用
solveHanoi方法进行求解。
通过这种方式,我们可以使用栈来模拟递归过程,从而解决汉诺塔问题。
14使用队列实现广度优先搜索(BFS)算法。
广度优先搜索(BFS)是一种遍历或搜索图的算法,它从根节点开始,首先访问所有相邻节点,然后再依次访问这些相邻节点的相邻节点。使用队列是实现BFS的常见方法,因为队列的先进先出(FIFO)特性非常适合这种逐层处理的需求。
下面是一个使用Java语言实现的示例代码,用于进行图的广度优先搜索:
import java.util.*; // 定义图的节点类 class Node { int val; List<Node> neighbors; Node(int val) { this.val = val; this.neighbors = new ArrayList<>(); } } public class BFS { // 方法来进行广度优先搜索 public static void breadthFirstSearch(Node startNode) { if (startNode == null) { return; } // 创建一个队列来存储节点 Queue<Node> queue = new LinkedList<>(); Set<Node> visited = new HashSet<>(); // 用于记录已访问的节点 queue.add(startNode); visited.add(startNode); while (!queue.isEmpty()) { // 取出队列头部的节点并打印其值 Node currentNode = queue.poll(); System.out.print(currentNode.val + " "); // 遍历当前节点的所有邻居节点 for (Node neighbor : currentNode.neighbors) { if (!visited.contains(neighbor)) { queue.add(neighbor); visited.add(neighbor); } } } } public static void main(String[] args) { // 构建一个示例图 Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); Node node4 = new Node(4); Node node5 = new Node(5); node1.neighbors.add(node2); node1.neighbors.add(node3); node2.neighbors.add(node4); node3.neighbors.add(node4); node4.neighbors.add(node5); // 调用广度优先搜索方法 System.out.println("Breadth First Search starting from node 1:"); breadthFirstSearch(node1); } }
- Node类:定义了图的节点结构,每个节点包含一个整数值和一个邻居列表。
- breadthFirstSearch方法:实现了广度优先搜索的逻辑。首先检查起始节点是否为空,如果为空则直接返回。然后创建一个队列和一个集合来分别存储待访问的节点和已访问的节点。将起始节点加入队列和集合中,然后进入循环,直到队列为空为止。在每次循环中,取出队列头部的节点并打印其值,然后遍历该节点的所有邻居节点,如果邻居节点未被访问过,则将其加入队列和集合中。
- main方法:构建了一个示例图,并调用
breadthFirstSearch方法从节点1开始进行广度优先搜索。
通过这种方式,我们可以有效地实现图的广度优先搜索。
15使用栈实现深度优先搜索(DFS)算法。
深度优先搜索(DFS)是一种遍历或搜索图的算法,它从根节点开始,沿着一个分支尽可能深入地搜索,直到不能再继续为止,然后回溯并探索其他分支。使用栈是实现DFS的常见方法,因为栈的后进先出(LIFO)特性非常适合这种回溯的需求。
下面是一个使用Java语言实现的示例代码,用于进行图的深度优先搜索:
import java.util.*; // 定义图的节点类 class Node { int val; List<Node> neighbors; Node(int val) { this.val = val; this.neighbors = new ArrayList<>(); } } public class DFS { // 方法来进行深度优先搜索 public static void depthFirstSearch(Node startNode) { if (startNode == null) { return; } // 创建一个栈来存储节点 Stack<Node> stack = new Stack<>(); Set<Node> visited = new HashSet<>(); // 用于记录已访问的节点 stack.push(startNode); visited.add(startNode); while (!stack.isEmpty()) { // 取出栈顶的节点并打印其值 Node currentNode = stack.pop(); System.out.print(currentNode.val + " "); // 遍历当前节点的所有邻居节点 for (Node neighbor : currentNode.neighbors) { if (!visited.contains(neighbor)) { stack.push(neighbor); visited.add(neighbor); } } } } public static void main(String[] args) { // 构建一个示例图 Node node1 = new Node(1); Node node2 = new Node(2); Node node3 = new Node(3); Node node4 = new Node(4); Node node5 = new Node(5); node1.neighbors.add(node2); node1.neighbors.add(node3); node2.neighbors.add(node4); node3.neighbors.add(node4); node4.neighbors.add(node5); // 调用深度优先搜索方法 System.out.println("Depth First Search starting from node 1:"); depthFirstSearch(node1); } }
- Node类:定义了图的节点结构,每个节点包含一个整数值和一个邻居列表。
- depthFirstSearch方法:实现了深度优先搜索的逻辑。首先检查起始节点是否为空,如果为空则直接返回。然后创建一个栈和一个集合来分别存储待访问的节点和已访问的节点。将起始节点加入栈和集合中,然后进入循环,直到栈为空为止。在每次循环中,取出栈顶的节点并打印其值,然后遍历该节点的所有邻居节点,如果邻居节点未被访问过,则将其加入栈和集合中。
- main方法:构建了一个示例图,并调用
depthFirstSearch方法从节点1开始进行深度优先搜索。
通过这种方式,我们可以有效地实现图的深度优先搜索。
16使用栈解决表达式求值问题(包括加减乘除和括号)。
表达式求值是计算机科学中的一个经典问题,通常使用栈来处理括号和运算符的优先级。我们可以将中缀表达式转换为后缀表达式(逆波兰表示法),然后使用栈来计算后缀表达式的值。
下面是一个使用Java语言实现的示例代码,用于解决包含加减乘除和括号的表达式求值问题:
import java.util.*; public class ExpressionEvaluator { // 定义运算符的优先级 private static final Map<Character, Integer> precedence = new HashMap<>(); static { precedence.put('+', 1); precedence.put('-', 1); precedence.put('*', 2); precedence.put('/', 2); } // 方法将中缀表达式转换为后缀表达式 private static List<String> infixToPostfix(String expression) { List<String> postfix = new ArrayList<>(); Stack<Character> stack = new Stack<>(); int i = 0; while (i < expression.length()) { char c = expression.charAt(i); if (Character.isDigit(c)) { StringBuilder number = new StringBuilder(); while (i < expression.length() && (Character.isDigit(expression.charAt(i)) || expression.charAt(i) == '.')) { number.append(expression.charAt(i++)); } postfix.add(number.toString()); continue; } else if (c == '(') { stack.push(c); } else if (c == ')') { while (!stack.isEmpty() && stack.peek() != '(') { postfix.add(stack.pop().toString()); } stack.pop(); // 弹出 '(' } else if (precedence.containsKey(c)) { while (!stack.isEmpty() && precedence.containsKey(stack.peek()) && precedence.get(stack.peek()) >= precedence.get(c)) { postfix.add(stack.pop().toString()); } stack.push(c); } i++; } while (!stack.isEmpty()) { postfix.add(stack.pop().toString()); } return postfix; } // 方法计算后缀表达式的值 private static double evaluatePostfix(List<String> postfix) { Stack<Double> stack = new Stack<>(); for (String token : postfix) { if (token.matches("\\d+(\\.\\d+)?")) { // 如果是数字 stack.push(Double.parseDouble(token)); } else { // 如果是运算符 double b = stack.pop(); double a = stack.pop(); switch (token) { case "+": stack.push(a + b); break; case "-": stack.push(a - b); break; case "*": stack.push(a * b); break; case "/": stack.push(a / b); break; } } } return stack.pop(); } // 主方法进行表达式求值 public static double evaluateExpression(String expression) { List<String> postfix = infixToPostfix(expression); return evaluatePostfix(postfix); } public static void main(String[] args) { String expression = "3 + (2 * 4) - (5 / 2)"; System.out.println("Expression: " + expression); double result = evaluateExpression(expression); System.out.println("Result: " + result); } }
- infixToPostfix方法:将中缀表达式转换为后缀表达式。我们使用一个栈来存储运算符,并按照运算符的优先级和括号规则进行处理。数字直接添加到后缀表达式列表中。
- evaluatePostfix方法:计算后缀表达式的值。我们使用一个栈来存储操作数,当遇到运算符时,从栈中弹出两个操作数进行计算,并将结果压回栈中。
- evaluateExpression方法:这是主方法,首先调用
infixToPostfix将中缀表达式转换为后缀表达式,然后调用evaluatePostfix计算后缀表达式的值。 - main方法:测试表达式求值功能。
通过这种方式,我们可以有效地解决包含加减乘除和括号的表达式求值问题。
17使用队列解决打印任务调度问题。
打印任务调度问题是一个经典的计算机科学问题,通常使用队列来管理多个打印任务。我们可以使用Java语言实现一个简单的打印任务调度系统,其中每个打印任务被表示为一个对象,并存储在一个队列中。
下面是一个示例代码,用于解决打印任务调度问题:
import java.util.LinkedList; import java.util.Queue; // 定义打印任务类 class PrintJob { private String documentName; private int numberOfPages; public PrintJob(String documentName, int numberOfPages) { this.documentName = documentName; this.numberOfPages = numberOfPages; } public String getDocumentName() { return documentName; } public int getNumberOfPages() { return numberOfPages; } @Override public String toString() { return "PrintJob{" + "documentName='" + documentName + '\'' + ", numberOfPages=" + numberOfPages + '}'; } } // 定义打印任务调度器类 class PrintScheduler { private Queue<PrintJob> printQueue; public PrintScheduler() { this.printQueue = new LinkedList<>(); } // 添加打印任务到队列 public void addPrintJob(PrintJob job) { printQueue.offer(job); System.out.println("Added: " + job); } // 处理下一个打印任务 public void processNextJob() { if (!printQueue.isEmpty()) { PrintJob job = printQueue.poll(); System.out.println("Processing: " + job); // 模拟打印过程(这里只是简单的输出) try { Thread.sleep(1000 * job.getNumberOfPages()); // 假设每页需要1秒打印时间 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Completed: " + job); } else { System.out.println("No jobs in the queue."); } } } public class Main { public static void main(String[] args) { PrintScheduler scheduler = new PrintScheduler(); // 添加一些打印任务到队列中 scheduler.addPrintJob(new PrintJob("Document1", 5)); scheduler.addPrintJob(new PrintJob("Document2", 3)); scheduler.addPrintJob(new PrintJob("Document3", 7)); // 处理所有打印任务 while (true) { scheduler.processNextJob(); try { Thread.sleep(1000); // 等待1秒再处理下一个任务 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- PrintJob类:定义了打印任务的属性,包括文档名称和页数。还重写了
toString方法以便于打印任务的显示。 - PrintScheduler类:使用一个队列来管理打印任务。提供了
addPrintJob方法来添加新的打印任务,以及processNextJob方法来处理队列中的下一个打印任务。 - Main类:创建了一个
PrintScheduler实例,并向队列中添加了一些打印任务。然后进入一个无限循环,不断处理队列中的打印任务。
通过这种方式,我们可以有效地管理和调度打印任务,确保它们按照顺序被处理。
18使用栈解决直方图中的最大矩形面积问题。
直方图中的最大矩形面积问题是一个经典的算法问题,通常使用栈来解决。这个问题的目标是找到直方图中最大的矩形面积。我们可以将每个柱子的高度存储在一个数组中,然后使用栈来帮助我们计算最大矩形面积。
下面是一个使用Java语言实现的示例代码:
import java.util.Stack; public class Histogram { // 方法计算直方图中的最大矩形面积 public static int maxHistogramArea(int[] heights) { Stack<Integer> stack = new Stack<>(); int maxArea = 0; int index = 0; while (index < heights.length) { // 如果当前柱子高度大于等于栈顶柱子高度,则将其索引入栈 if (stack.isEmpty() || heights[index] >= heights[stack.peek()]) { stack.push(index++); } else { // 否则,弹出栈顶元素并计算以该柱子为高的矩形面积 int top = stack.pop(); int area = heights[top] * (stack.isEmpty() ? index : index - stack.peek() - 1); maxArea = Math.max(maxArea, area); } } // 处理栈中剩余的元素 while (!stack.isEmpty()) { int top = stack.pop(); int area = heights[top] * (stack.isEmpty() ? index : index - stack.peek() - 1); maxArea = Math.max(maxArea, area); } return maxArea; } public static void main(String[] args) { int[] heights = {2, 1, 5, 6, 2, 3}; System.out.println("Maximum area in histogram: " + maxHistogramArea(heights)); // 输出应为10 } }
- maxHistogramArea方法:这个方法接受一个整数数组
heights,表示直方图的每个柱子的高度。我们使用一个栈来存储柱子的索引,以便在需要时计算矩形面积。- 我们遍历每个柱子的高度。如果当前柱子的高度大于等于栈顶柱子的高度,我们将当前柱子的索引入栈。
- 如果当前柱子的高度小于栈顶柱子的高度,我们从栈中弹出顶部元素,并计算以该柱子为高的矩形面积。这个面积取决于当前柱子的索引和栈顶元素的索引之间的差值。
- 最后,我们处理栈中剩余的元素,以确保所有柱子都被考虑在内。
- main方法:测试了
maxHistogramArea方法,输出直方图中的最大矩形面积。
通过这种方式,我们可以有效地解决直方图中的最大矩形面积问题。
19使用队列解决滑动窗口的最小值问题。
滑动窗口的最小值问题是一个经典的算法问题,通常使用双端队列(Deque)来解决。这个问题的目标是在给定数组中找到一个长度为
k的滑动窗口,使得每个窗口中的最小值都能被高效地找到。下面是一个使用Java语言实现的示例代码:
import java.util.ArrayDeque; import java.util.Deque; public class SlidingWindowMinimum { // 方法计算滑动窗口的最小值 public static int[] minSlidingWindow(int[] nums, int k) { if (nums == null || nums.length == 0 || k <= 0) { return new int[0]; } int n = nums.length; int[] result = new int[n - k + 1]; // 结果数组 Deque<Integer> deque = new ArrayDeque<>(); // 双端队列 for (int i = 0; i < n; i++) { // 移除超出窗口范围的元素索引 while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) { deque.pollFirst(); } // 移除所有大于当前元素的索引,以保持队列单调递增 while (!deque.isEmpty() && nums[deque.peekLast()] > nums[i]) { deque.pollLast(); } // 将当前元素索引添加到队列中 deque.offerLast(i); // 当窗口形成时,记录当前窗口的最小值 if (i >= k - 1) { result[i - k + 1] = nums[deque.peekFirst()]; } } return result; } public static void main(String[] args) { int[] nums = {1, 3, -1, -3, 5, 3, 6, 7}; int k = 3; int[] result = minSlidingWindow(nums, k); System.out.print("Sliding window minimum values: "); for (int val : result) { System.out.print(val + " "); } } }
- minSlidingWindow方法:这个方法接受一个整数数组
nums和一个整数k,表示滑动窗口的大小。它返回一个包含每个滑动窗口最小值的数组。- 我们首先检查输入是否有效。如果无效,则返回一个空数组。
- 我们创建一个双端队列
deque来存储数组元素的索引,并确保队列中的元素是按值递增的。 - 我们遍历数组,对于每个元素:
- 移除队列中所有超出当前窗口范围的元素索引。
- 移除队列中所有大于当前元素的索引,以保持队列的单调递增性质。
- 将当前元素的索引添加到队列中。
- 当窗口形成时(即索引
i大于等于k-1),我们将当前窗口的最小值(即队列头部的元素)添加到结果数组中。
- main方法:测试了
minSlidingWindow方法,输出滑动窗口的最小值。
通过这种方式,我们可以高效地解决滑动窗口的最小值问题。
20使用栈实现图的深度优先遍历。
深度优先搜索(DFS)是一种用于遍历或搜索图的算法。使用栈来实现DFS是一种常见的方法,因为栈可以帮助我们追踪访问路径并回溯到之前的节点。
下面是一个使用Java语言实现的示例代码,展示如何使用栈来进行图的深度优先遍历:
import java.util.*; public class Graph { private int numVertices; // 图中顶点的数量 private LinkedList<Integer>[] adjLists; // 邻接表表示法 private boolean[] visited; // 记录每个顶点是否被访问过 // 构造函数 public Graph(int vertices) { numVertices = vertices; adjLists = new LinkedList[vertices]; for (int i = 0; i < vertices; i++) { adjLists[i] = new LinkedList<>(); } visited = new boolean[vertices]; } // 添加边 void addEdge(int src, int dest) { adjLists[src].add(dest); } // 深度优先搜索(使用栈) void DFS(int startVertex) { Stack<Integer> stack = new Stack<>(); stack.push(startVertex); while (!stack.isEmpty()) { int vertex = stack.pop(); if (!visited[vertex]) { visited[vertex] = true; System.out.print(vertex + " "); // 将所有未访问的相邻节点压入栈中 Iterator<Integer> it = adjLists[vertex].listIterator(); while (it.hasNext()) { int adj = it.next(); if (!visited[adj]) { stack.push(adj); } } } } } public static void main(String[] args) { Graph g = new Graph(6); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 3); g.addEdge(1, 4); g.addEdge(2, 4); g.addEdge(3, 5); g.addEdge(4, 5); System.out.println("Depth First Traversal starting from vertex 0:"); g.DFS(0); } }
- Graph类:这个类表示一个图,包含顶点数量、邻接表和访问标记数组。
numVertices:图中顶点的数量。adjLists:邻接表,用于存储每个顶点的相邻顶点。visited:布尔数组,用于记录每个顶点是否已被访问。
- addEdge方法:这个方法用于在图中添加一条边。
src:边的起始顶点。dest:边的终止顶点。
- DFS方法:这个方法实现了深度优先搜索,使用栈来管理待访问的顶点。
startVertex:开始遍历的顶点。stack:用于存储待访问的顶点。- 当栈不为空时,弹出栈顶元素并检查是否已访问。如果未访问,则标记为已访问并输出该顶点。然后将其所有未访问的相邻顶点压入栈中。
- main方法:创建一个图并添加一些边,然后从顶点0开始进行深度优先遍历。
通过这种方式,我们可以使用栈来实现图的深度优先遍历。
21使用队列实现图的广度优先遍历。
广度优先搜索(BFS)是一种用于遍历或搜索图的算法。使用队列来实现BFS是一种常见的方法,因为队列可以帮助我们按层次顺序访问节点。
下面是一个使用Java语言实现的示例代码,展示如何使用队列来进行图的广度优先遍历:
import java.util.*; public class Graph { private int numVertices; // 图中顶点的数量 private LinkedList<Integer>[] adjLists; // 邻接表表示法 private boolean[] visited; // 记录每个顶点是否被访问过 // 构造函数 public Graph(int vertices) { numVertices = vertices; adjLists = new LinkedList[vertices]; for (int i = 0; i < vertices; i++) { adjLists[i] = new LinkedList<>(); } visited = new boolean[vertices]; } // 添加边 void addEdge(int src, int dest) { adjLists[src].add(dest); } // 广度优先搜索(使用队列) void BFS(int startVertex) { Queue<Integer> queue = new LinkedList<>(); visited[startVertex] = true; queue.add(startVertex); while (!queue.isEmpty()) { int vertex = queue.poll(); System.out.print(vertex + " "); Iterator<Integer> it = adjLists[vertex].listIterator(); while (it.hasNext()) { int adj = it.next(); if (!visited[adj]) { visited[adj] = true; queue.add(adj); } } } } public static void main(String[] args) { Graph g = new Graph(6); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 3); g.addEdge(1, 4); g.addEdge(2, 4); g.addEdge(3, 5); g.addEdge(4, 5); System.out.println("Breadth First Traversal starting from vertex 0:"); g.BFS(0); } }
- Graph类:这个类表示一个图,包含顶点数量、邻接表和访问标记数组。
numVertices:图中顶点的数量。adjLists:邻接表,用于存储每个顶点的相邻顶点。visited:布尔数组,用于记录每个顶点是否已被访问。
- addEdge方法:这个方法用于在图中添加一条边。
src:边的起始顶点。dest:边的终止顶点。
- BFS方法:这个方法实现了广度优先搜索,使用队列来管理待访问的顶点。
startVertex:开始遍历的顶点。queue:用于存储待访问的顶点。- 当队列不为空时,从队列中取出队首元素并检查是否已访问。如果未访问,则标记为已访问并输出该顶点。然后将其所有未访问的相邻顶点加入队列中。
- main方法:创建一个图并添加一些边,然后从顶点0开始进行广度优先遍历。
通过这种方式,我们可以使用队列来实现图的广度优先遍历。
22使用栈解决迷宫问题。
解决迷宫问题通常使用深度优先搜索(DFS)算法,而栈是实现DFS的一种自然选择。下面是一个使用Java语言实现的示例代码,展示如何使用栈来解决迷宫问题:
import java.util.Stack; public class MazeSolver { private static final int[] ROW_MOVES = {-1, 0, 1, 0}; // 上、右、下、左移动 private static final int[] COL_MOVES = {0, 1, 0, -1}; // 检查当前位置是否在迷宫范围内且可走 private static boolean isValidMove(int[][] maze, boolean[][] visited, int row, int col) { return (row >= 0 && row < maze.length && col >= 0 && col < maze[0].length && maze[row][col] == 1 && !visited[row][col]); } // 打印路径 private static void printPath(Stack<int[]> path) { for (int[] pos : path) { System.out.print("(" + pos[0] + ", " + pos[1] + ") "); } System.out.println(); } // 使用栈解决迷宫问题 public static boolean solveMaze(int[][] maze, int startRow, int startCol, int endRow, int endCol) { Stack<int[]> stack = new Stack<>(); boolean[][] visited = new boolean[maze.length][maze[0].length]; stack.push(new int[]{startRow, startCol}); visited[startRow][startCol] = true; while (!stack.isEmpty()) { int[] current = stack.pop(); int row = current[0]; int col = current[1]; // 如果到达终点,打印路径并返回true if (row == endRow && col == endCol) { System.out.println("Path to the exit:"); printPath(stack); System.out.println("(" + row + ", " + col + ")"); return true; } // 尝试所有可能的移动方向 for (int i = 0; i < 4; i++) { int newRow = row + ROW_MOVES[i]; int newCol = col + COL_MOVES[i]; if (isValidMove(maze, visited, newRow, newCol)) { stack.push(new int[]{newRow, newCol}); visited[newRow][newCol] = true; } } } // 如果没有找到路径,返回false return false; } public static void main(String[] args) { int[][] maze = { {1, 0, 0, 0}, {1, 1, 0, 1}, {0, 1, 0, 0}, {1, 1, 1, 1} }; int startRow = 0; int startCol = 0; int endRow = 3; int endCol = 3; if (!solveMaze(maze, startRow, startCol, endRow, endCol)) { System.out.println("No path found."); } } }
- isValidMove方法:这个方法用于检查当前位置是否在迷宫范围内且可走。迷宫中的
1表示可以走,0表示障碍物。maze:迷宫矩阵。visited:布尔数组,记录每个位置是否已被访问。row和col:当前位置的行和列。
- printPath方法:这个方法用于打印从起点到终点的路径。
path:存储路径的栈。
- solveMaze方法:这个方法实现了使用栈解决迷宫问题。
maze:迷宫矩阵。startRow和startCol:起点的行和列。endRow和endCol:终点的行和列。stack:用于存储待访问的位置。visited:布尔数组,记录每个位置是否已被访问。- 当栈不为空时,弹出栈顶元素并检查是否已到达终点。如果到达终点,则打印路径并返回
true。否则,尝试所有可能的移动方向,并将有效的新位置压入栈中。
- main方法:创建一个迷宫并指定起点和终点,然后调用
solveMaze方法进行求解。如果没有找到路径,输出“没有找到路径”。
通过这种方式,我们可以使用栈来实现迷宫问题的求解。
23使用队列解决课程表问题。
课程表问题(Course Schedule Problem)通常涉及判断是否可以完成所有课程,其中每门课程可能有一些前置课程。这个问题可以通过拓扑排序来解决,而队列是实现拓扑排序的一种自然选择。
下面是一个使用Java语言实现的示例代码,展示如何使用队列解决课程表问题:
import java.util.*; public class CourseSchedule { // 检查是否可以完成所有课程 public boolean canFinish(int numCourses, int[][] prerequisites) { // 构建图和入度数组 List<List<Integer>> graph = new ArrayList<>(); int[] inDegree = new int[numCourses]; for (int i = 0; i < numCourses; i++) { graph.add(new ArrayList<>()); } for (int[] prerequisite : prerequisites) { graph.get(prerequisite[1]).add(prerequisite[0]); inDegree[prerequisite[0]]++; } // 使用队列进行拓扑排序 Queue<Integer> queue = new LinkedList<>(); for (int i = 0; i < numCourses; i++) { if (inDegree[i] == 0) { queue.add(i); } } int count = 0; while (!queue.isEmpty()) { int course = queue.poll(); count++; for (int nextCourse : graph.get(course)) { inDegree[nextCourse]--; if (inDegree[nextCourse] == 0) { queue.add(nextCourse); } } } return count == numCourses; } public static void main(String[] args) { CourseSchedule cs = new CourseSchedule(); int numCourses = 4; int[][] prerequisites = {{1, 0}, {2, 1}, {3, 2}}; System.out.println("Can finish all courses: " + cs.canFinish(numCourses, prerequisites)); // true int numCourses2 = 4; int[][] prerequisites2 = {{1, 0}, {2, 1}, {3, 2}, {1, 3}}; System.out.println("Can finish all courses: " + cs.canFinish(numCourses2, prerequisites2)); // false } }
- 构建图和入度数组:
graph:邻接表表示的有向图,graph.get(i)存储的是第i门课的所有后续课程。inDegree:入度数组,inDegree[i]表示第i门课的前置课程数量。- 遍历
prerequisites数组,填充graph和inDegree。
- 使用队列进行拓扑排序:
- 初始化一个队列
queue,将所有入度为0的课程加入队列。 - 当队列不为空时,取出队首元素(当前课程),将其计数器
count加1。 - 遍历当前课程的所有后续课程,将它们的入度减1。如果某个后续课程的入度变为0,则将其加入队列。
- 最后检查是否所有课程都被处理过(即
count是否等于numCourses)。
- 初始化一个队列
- 主方法:
- 创建
CourseSchedule对象并测试两个例子,分别输出是否可以完成所有课程。
- 创建
通过这种方式,我们可以使用队列来实现课程表问题的求解。
24使用栈解决股票买卖问题。
股票买卖问题(Stock Buy and Sell Problem)通常涉及找到最佳的买入和卖出时机,以最大化利润。这个问题可以通过一次遍历来解决,使用栈来记录可能的买入点。
下面是一个使用Java语言实现的示例代码,展示如何使用栈解决股票买卖问题:
import java.util.Stack; public class StockBuySell { // 计算最大利润的方法 public static int maxProfit(int[] prices) { Stack<Integer> stack = new Stack<>(); int maxProfit = 0; for (int i = 0; i < prices.length; i++) { // 如果栈为空或者当前价格小于等于栈顶元素,则将当前价格入栈 if (stack.isEmpty() || prices[i] <= prices[stack.peek()]) { stack.push(i); } else { // 否则,计算当前价格与栈顶元素的价格差值,并更新最大利润 while (!stack.isEmpty() && prices[i] > prices[stack.peek()]) { int buyPrice = prices[stack.pop()]; maxProfit += prices[i] - buyPrice; } // 将当前价格入栈 stack.push(i); } } return maxProfit; } public static void main(String[] args) { int[] prices = {7, 1, 5, 3, 6, 4}; System.out.println("Maximum profit: " + maxProfit(prices)); // 输出:7 int[] prices2 = {1, 2, 3, 4, 5}; System.out.println("Maximum profit: " + maxProfit(prices2)); // 输出:4 int[] prices3 = {7, 6, 4, 3, 1}; System.out.println("Maximum profit: " + maxProfit(prices3)); // 输出:0 } }
- maxProfit方法:这个方法用于计算最大利润。
prices:股票价格数组。stack:用于存储可能的买入点的栈。maxProfit:用于记录最大利润。- 遍历
prices数组,对于每个价格:- 如果栈为空或者当前价格小于等于栈顶元素的价格,则将当前价格的索引入栈。
- 否则,计算当前价格与栈顶元素的价格差值,并更新最大利润。然后继续检查下一个栈顶元素,直到找到一个比当前价格小的元素或栈为空为止。最后将当前价格的索引入栈。
- main方法:创建
StockBuySell对象并测试三个例子,分别输出最大利润。
通过这种方式,我们可以使用栈来实现股票买卖问题的求解。
25使用队列解决汽车排队加油问题。
汽车排队加油问题(Car Fueling Problem)通常涉及计算最少的加油次数,以确保所有汽车都能在加油站完成加油。这个问题可以通过贪心算法来解决,使用队列来模拟汽车的加油过程。
下面是一个使用Java语言实现的示例代码,展示如何使用队列解决汽车排队加油问题:
import java.util.LinkedList; import java.util.Queue; public class CarFueling { // 计算最少加油次数的方法 public static int minRefuelStops(int target, int startFuel, int[][] stations) { Queue<Integer> queue = new LinkedList<>(); int currentFuel = startFuel; int count = 0; int index = 0; while (currentFuel < target) { // 将所有可以到达的加油站加入队列 while (index < stations.length && stations[index][0] <= currentFuel) { queue.add(stations[index][1]); index++; } // 如果队列为空,说明无法到达目标位置 if (queue.isEmpty()) { return -1; } // 从队列中取出最大的燃料量进行加油 currentFuel += queue.poll(); count++; } return count; } public static void main(String[] args) { int target = 100; int startFuel = 10; int[][] stations = {{10, 60}, {20, 30}, {30, 30}, {60, 40}}; System.out.println("Minimum refuel stops: " + minRefuelStops(target, startFuel, stations)); // 输出:2 target = 100; startFuel = 50; stations = new int[][]{{25, 25}, {50, 50}}; System.out.println("Minimum refuel stops: " + minRefuelStops(target, startFuel, stations)); // 输出:1 target = 100; startFuel = 10; stations = new int[][]{{10, 100}}; System.out.println("Minimum refuel stops: " + minRefuelStops(target, startFuel, stations)); // 输出:1 target = 100; startFuel = 10; stations = new int[][]{{10, 20}, {20, 30}, {30, 40}}; System.out.println("Minimum refuel stops: " + minRefuelStops(target, startFuel, stations)); // 输出:-1 } }
- minRefuelStops方法:这个方法用于计算最少加油次数。
target:目标距离。startFuel:初始油量。stations:二维数组,每个元素表示一个加油站的位置和该站能加的油量。queue:用于存储可以到达的加油站的油量。currentFuel:当前油量。count:加油次数计数器。index:当前处理的加油站索引。- 当
currentFuel小于target时,循环执行以下操作:- 将所有可以到达的加油站的油量加入队列。
- 如果队列为空,说明无法到达目标位置,返回-1。
- 从队列中取出最大的油量进行加油,并增加加油次数计数器。
- 最后返回加油次数计数器的值。
- main方法:创建
CarFueling对象并测试四个例子,分别输出最少加油次数。
通过这种方式,我们可以使用队列来实现汽车排队加油问题的求解。
26使用栈实现撤销操作。
在Java中,可以使用栈(Stack)来实现撤销操作。栈是一种后进先出(LIFO)的数据结构,非常适合用于实现撤销功能,因为每次撤销操作都相当于从栈顶取出最近一次的操作。
下面是一个简单的示例,展示如何使用栈来实现撤销操作:
import java.util.Stack; class Command { private String action; public Command(String action) { this.action = action; } public void execute() { // 执行具体的命令操作 System.out.println("Executing: " + action); } public void undo() { // 撤销具体的命令操作 System.out.println("Undoing: " + action); } } class CommandManager { private Stack<Command> commandStack = new Stack<>(); private Stack<Command> redoStack = new Stack<>(); public void executeCommand(Command command) { command.execute(); commandStack.push(command); redoStack.clear(); // 清除重做栈,因为新命令执行后,之前的重做历史无效 } public void undo() { if (!commandStack.isEmpty()) { Command command = commandStack.pop(); command.undo(); redoStack.push(command); } else { System.out.println("No commands to undo"); } } public void redo() { if (!redoStack.isEmpty()) { Command command = redoStack.pop(); command.execute(); commandStack.push(command); } else { System.out.println("No commands to redo"); } } } public class Main { public static void main(String[] args) { CommandManager manager = new CommandManager(); Command command1 = new Command("Action 1"); Command command2 = new Command("Action 2"); Command command3 = new Command("Action 3"); manager.executeCommand(command1); manager.executeCommand(command2); manager.executeCommand(command3); System.out.println("Undo last command:"); manager.undo(); System.out.println("Redo last undone command:"); manager.redo(); } }
在这个示例中:
Command类表示一个可执行和可撤销的命令。每个命令都有一个execute方法和一个undo方法。CommandManager类管理命令的执行、撤销和重做。它使用两个栈:commandStack用于存储已执行的命令,以便可以撤销;redoStack用于存储已撤销的命令,以便可以重做。Main类演示了如何使用CommandManager来执行、撤销和重做命令。
运行这个程序将输出以下内容:
Executing: Action 1 Executing: Action 2 Executing: Action 3 Undo last command: Undoing: Action 3 Redo last undone command: Executing: Action 3
27使用队列实现打印任务调度。
在Java中,可以使用
Queue接口及其实现类(如LinkedList)来实现打印任务调度。以下是一个简单的示例,展示如何使用队列来管理打印任务:import java.util.LinkedList; import java.util.Queue; class PrintJob { private String documentName; private int numberOfPages; public PrintJob(String documentName, int numberOfPages) { this.documentName = documentName; this.numberOfPages = numberOfPages; } public String getDocumentName() { return documentName; } public int getNumberOfPages() { return numberOfPages; } @Override public String toString() { return "PrintJob{" + "documentName='" + documentName + '\'' + ", numberOfPages=" + numberOfPages + '}'; } } class Printer { private Queue<PrintJob> printQueue = new LinkedList<>(); public void addJob(PrintJob job) { printQueue.add(job); System.out.println("Added job: " + job); } public void processJobs() { while (!printQueue.isEmpty()) { PrintJob currentJob = printQueue.poll(); System.out.println("Processing job: " + currentJob); // Simulate printing time try { Thread.sleep(currentJob.getNumberOfPages() * 100); // Assuming each page takes 100ms to print } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Completed job: " + currentJob); } } } public class Main { public static void main(String[] args) { Printer printer = new Printer(); PrintJob job1 = new PrintJob("Document1", 5); PrintJob job2 = new PrintJob("Document2", 3); PrintJob job3 = new PrintJob("Document3", 7); printer.addJob(job1); printer.addJob(job2); printer.addJob(job3); printer.processJobs(); } }
27.1代码解释:
- PrintJob类:表示一个打印任务,包含文档名称和页数。
- Printer类:管理打印任务的队列。提供添加任务的方法
addJob和处理任务的方法processJobs。addJob方法将新的打印任务添加到队列中。processJobs方法从队列中取出任务并模拟打印过程。
- Main类:测试打印任务调度系统。创建几个打印任务并将其添加到打印机的任务队列中,然后处理这些任务。
27.2运行结果:
程序会依次处理每个打印任务,并在控制台输出相关信息。例如:
Added job: PrintJob{documentName='Document1', numberOfPages=5} Added job: PrintJob{documentName='Document2', numberOfPages=3} Added job: PrintJob{documentName='Document3', numberOfPages=7} Processing job: PrintJob{documentName='Document1', numberOfPages=5} Completed job: PrintJob{documentName='Document1', numberOfPages=5} Processing job: PrintJob{documentName='Document2', numberOfPages=3} Completed job: PrintJob{documentName='Document2', numberOfPages=3} Processing job: PrintJob{documentName='Document3', numberOfPages=7} Completed job: PrintJob{documentName='Document3', numberOfPages=7}
这个示例展示了如何使用队列来管理和调度打印任务。你可以根据实际需求进一步扩展和优化这个示例。
28使用队列实现层次遍历二叉树。
层次遍历(也称为广度优先搜索,BFS)是一种遍历二叉树的方式,它按层级从上到下、从左到右访问节点。使用队列是实现层次遍历的常见方法,因为队列遵循先进先出(FIFO)的原则,非常适合逐层处理节点。
以下是使用Java语言实现层次遍历二叉树的示例代码:
import java.util.LinkedList; import java.util.Queue; class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class BinaryTreeLevelOrderTraversal { public static void levelOrderTraversal(TreeNode root) { if (root == null) { return; } Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { TreeNode currentNode = queue.poll(); System.out.print(currentNode.val + " "); if (currentNode.left != null) { queue.add(currentNode.left); } if (currentNode.right != null) { queue.add(currentNode.right); } } } public static void main(String[] args) { // 创建一个示例二叉树 TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.right = new TreeNode(3); root.left.left = new TreeNode(4); root.left.right = new TreeNode(5); root.right.left = new TreeNode(6); root.right.right = new TreeNode(7); // 执行层次遍历 System.out.println("层次遍历结果:"); levelOrderTraversal(root); } }
28.1代码解释:
- TreeNode类:定义了二叉树的节点结构,每个节点包含一个整数值
val以及指向左右子节点的引用left和right。 - levelOrderTraversal方法:实现了层次遍历的逻辑。
- 如果根节点为空,直接返回。
- 使用一个队列来存储待处理的节点。首先将根节点加入队列。
- 当队列不为空时,循环处理队列中的节点:
- 取出队首节点并打印其值。
- 如果该节点有左子节点,将其加入队列。
- 如果该节点有右子节点,将其加入队列。
- main方法:创建一个示例二叉树并调用
levelOrderTraversal方法进行层次遍历。
28.2运行结果:
程序会输出二叉树的层次遍历结果:
层次遍历结果: 1 2 3 4 5 6 7
这个示例展示了如何使用队列来实现二叉树的层次遍历。你可以根据需要修改或扩展这个示例以适应不同的应用场景。