目录:
Stack & Queue
005-用两个栈实现队列 √
020-包含min函数的栈 √
021-栈的压入、弹出序列 √
044-翻转单词顺序列(栈) √
043-左旋转字符串(矩阵翻转) √
064-滑动窗口的最大值 √
Heap
029-最小的K个数 √
Hash Table
034-第一个只出现一次的字符 √
图 (图的遍历 + 回溯法)
065-矩阵中的路径(BFS) √
066-机器人的运动范围(DFS) √
Stack & Queue
005-用两个栈实现队列 √
题目描述: 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路:
栈 ---先入后出 ;队列 --- 先入先出 1. 一个栈负责入队,另一个负责出队,出栈为空则从入栈中导入到出栈中 2. 相当于每次弹出前,先把栈掉了一个顺序,然后再把栈尾(队头)pop出来
解题代码:(Java)
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack2.empty()){
while (!stack1.empty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
020-包含min函数的栈 √
题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
思路: 1. 因为要求时间复杂度为 O(1) 自然想到用额外空间换时间 2. 创建两个 Stack: 1 - dataStack 正常顺序存储堆栈;2 - minStack 存当前堆栈最小值 3. push时判定 新加入的 node 是否比 minStack.peek() 小,如果小则 minStack.push(node) 4. 否则 minStack.push(minStack.peek()) // 相当于新加入的node比之前的min大, minStack中存入一个之前的min值(minStack.peek())
解题代码:(java)
import java.util.Stack;
public class Solution {
Stack<Integer> dataStack = new Stack<Integer>();
Stack<Integer> minStack = new Stack<Integer>();
public void push(int node) {
dataStack.push(node);
if(minStack.isEmpty() || node < minStack.peek())
minStack.push(node);
// add the old peek value as new peak value (min value)
else
minStack.push(minStack.peek());
}
public void pop() {
dataStack.pop();
minStack.pop();
}
// get top node value in Stack
public int top() {
return dataStack.peek();
}
// get min node value in Stack
public int min() {
return minStack.peek();
}
}
021-栈的压入、弹出序列 √
题目描述 : 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
给定栈的压入序列,判断某序列是否可能为栈的弹出序列是一道笔试中常考的选择题,那么如何用代码来判定呢?
思路: 1. 循环,找和pop序列第一个元素相同的值 - 找到,则 stack.pop(),indexPop后移 - 未找到,则 stack.push(pushA[indexPush]) 2. 退出条件:如果弹出序列指针还没到结尾但已经无元素可压入,则被测序列不是弹出序列。 3. 循环结束依然没有退出,返回 true
解题代码:(java)
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA == null || popA == null || pushA.length != popA.length)
return false;
Stack<Integer> stack = new Stack<Integer>();
int indexPush = 0;
int indexPop = 0;
while (indexPop < popA.length){
// Do not find the equal value
if (stack.isEmpty() || stack.peek() != popA[indexPop]){
// the push sequence is not clear
if(indexPush < pushA.length){
stack.push(pushA[indexPush]);
indexPush += 1;
}
else
return false;
}
// Find the equal value
else{
stack.pop();
indexPop += 1;
}
}
return true;
}
}
044-翻转单词顺序列(栈) √
题目描述:句子中单词翻转,但是单词里的字母不翻转 “student. a am I” --> “I am a student.”
思路: 1. 先把整个字符串反转 2. 再将每一个单词分别反转会原状
解题代码:(java)
import java.util.Stack;
public class Solution {
// reverse a whole sentence but do not reverse letters in a word.
public String ReverseSentence(String str) {
// using str.trim().equals("") to remove multiple space
if(str==null||str.trim().equals(""))
return str;
String reverseStr = Reverse(str);
String [] words = reverseStr.split(" ");
StringBuffer sb = new StringBuffer();
for(int i = 0; i< words.length-1;i++){
sb.append(Reverse(words[i])+" ");
}
// Note : the last word without space symbol
sb.append(Reverse(words[words.length-1]));
return sb.toString();
}
public String Reverse(String str){
StringBuffer sb = new StringBuffer();
int index = 0;
Stack<Character> stack = new Stack<Character>();
for(;index < str.length();index++){
stack.push(str.charAt(index));
}
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.toString();
}
}
043-左旋转字符串(矩阵翻转) √
题目描述:对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”
思路: 1. 直接两个for循环反转就行,时间复杂度 O(n) 2. 注意当传入值 n > str.length() 时做取模操作
解题代码:(java)
public class Solution {
public String LeftRotateString(String str,int n) {
if(str==null||str.trim().equals("")|| n < 0)
return str;
if(n > str.length())
n = n % str.length();
StringBuffer sb = new StringBuffer();
int i;
for(i = n;i<str.length();i++){
sb.append(str.charAt(i));
}
for(i=0;i<n;i++){
sb.append(str.charAt(i));
}
return sb.toString();
}
}
064-滑动窗口的最大值 √
题目描述 : 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}
解法1 - 暴力循环法 时间复杂度 O(n*size),空间复杂度O(1) 解题代码 (Java):
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res = new ArrayList<Integer>();
if(num.length<=0 || size <= 0)
return res;
int i = 0;
for(; i<num.length;i++){
int j = 1;
int max = num[i];
for(;j<size && j+i<num.length;j++){
if(num[i+j] > max)
max = num[i+j];
}
// avoid beyond the range
if(size+i<num.length+1)
res.add(max);
}
return res;
}
}
- [ ] 解法2 - 双端队列 待补充
解题代码:(java)
Heap
029-最小的K个数 *** √
题目描述 : 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
这是典型的TopK问题,常见的几种解法有: 1. 全排序,然后取出前K位。但是这在数据量很大的情况下很明显是做了后K位数字排序的无用功。时间复杂度为O(nlogn),空间复杂度为O(1) 2. 堆排序,创建一个长度为K的堆,TopMaxK则用最大堆,TopMinK则用最小堆,并且不会更改原数组,适合处理海量数据情况。时间复杂度为O(nlogk),空间复杂度为O(k)
解题代码 1-优先级实现 minHeap:(java)
import java.util.PriorityQueue;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
if (k > input.length || k <= 0)
return new ArrayList<>();
// Max Heap
//PriorityQueue<Integer> maxHeap = new PriorityQueue<>();
// Min Heap
PriorityQueue<Integer> minHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
for (int num : input) {
minHeap.add(num);
if (minHeap.size() > k)
minHeap.poll();
}
return new ArrayList<>(minHeap);
}
}
- [ ] 解题代码 2-手撸Heap :(java)
Hash Table
034-第一个只出现一次的字符 √
题目描述 : 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
思路:利用一个额外数组times[char]存储str中不同字符的个数,顺序存储第二次扫描时遇到的第一个 times[char] == 1 的 char所在str中的index即为答案
解题代码 1- int [] (java):
public class T34_FirstNotRepeatingChar {
public int FirstNotRepeatingChar(String str) {
if(str == null)
return -1;
int [] times = new int[256];
for (int i = 0; i<str.length();i++){
System.out.println(str.charAt(i));
times[str.charAt(i)]+=1;
}
for (int i = 0; i<str.length();i++){
System.out.println(str.charAt(i) + " : "+times[str.charAt(i)]);
if (times[str.charAt(i)] == 1)
return str.indexOf(str.charAt(i));
}
return -1;
}
}
解题代码 2- HashMap (Java):
import java.util.HashMap;
public class T34_FirstNotRepeatingChar {
public int FirstNotRepeatingCharHashMap(String str){
HashMap<Character, Integer> map = new HashMap<>();
if (str==null)
return -1;
int length = str.length();
for(int i = 0;i<length;i++) {
if(map.containsKey(str.charAt(i))){
int value = map.get(str.charAt(i));
map.put(str.charAt(i),value+1);
}else{
map.put(str.charAt(i),1);
}
}
for(int i = 0;i<length;i++){
if(map.get(str.charAt(i))==1)
return i;
}
return -1;
}
}
图 (图的遍历 + 回溯法)
065-矩阵中的路径(BFS) √
题目描述 : 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
回溯法思路: 1. 从0 rows 0 cols 开始 坐标 (i,j) index依次加一判定 ( index = i * rows + j) 2. 需要一个flag数组记录 当前位置有没有被走过
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
boolean [] flags = new boolean[matrix.length]; // gone position is true
int position = 0;
for(int i =0; i<rows; i++){
for(int j =0; j < cols; j++){
if(findPath(matrix,rows,cols,i,j,position,str,flags))
return true;
}
}
return false;
}
public boolean findPath(char[] matrix, int rows, int cols, int i, int j,int position,char[] str,boolean[] flags){
int curIndex = i*rows+j;
if(i<0 || i>=rows|| j<0 || j>= cols || matrix[curIndex] != str[position] || flags[curIndex])
return false;
if(position == str.length -1)
return true;
flags[curIndex] = true; // mark (i,j) as true
if(findPath(matrix,rows,cols,i+1,j,position+1,str,flags)||
findPath(matrix,rows,cols,i-1,j,position+1,str,flags)||
findPath(matrix,rows,cols,i,j+1,position+1,str,flags)||
findPath(matrix,rows,cols,i,j-1,position+1,str,flags))
return true;
// no possibility to find path at current index, so mark flags[curIndex] = false
flags[curIndex] = false;
return false;
}
}
066-机器人的运动范围(DFS) √
题目描述: 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路 1: 回溯 + 递归 1. 定义一个二维数组 flags 来标记走过的格子,这是之后递归函数退出的条件之一 2. 定义一个函数 sum 计算当前方格坐标阈值(行坐标和列坐标的数位之和),这也是之后递归函数退出的条件之一 3. 根据题意构造回溯方法,返回值为满足条件的格子数 4. 按照上下左右顺序,递归调用回溯方法,只要满足则count值+1
递归解题代码:(java)
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
int[][] flags = new int[rows][cols];
return count(0,0,threshold,rows,cols,flags);
}
private int count(int i, int j, int threshold, int rows, int cols,int[][] flags) {
if(i<0 || j<0 || i>=rows || j>=cols || sum(i,j)>threshold || flags[i][j] == 1)
return 0;
flags[i][j] = 1;
// 按照上下左右格子的顺序递归调用回溯方法
return count(i-1,j,threshold,rows,cols,flags)
+ count(i+1,j,threshold,rows,cols,flags)
+ count(i,j-1,threshold,rows,cols,flags)
+ count(i,j+1,threshold,rows,cols,flags)
+ 1;// 当前格子满足条件,返回值+1
}
private int sum(int i, int j){
int sum = 0;
do {
sum += i%10;
i = i/10;
}while (i>0);
do {
sum += j%10;
j = j/10;
}while (j>0);
return sum;
}
}
思路 2: 回溯 + 非递归 1. 定义一个 flags 来标记走过的格子,这是之后递归函数退出的条件之一 2. 定义一个函数 sum 计算当前方格坐标阈值(行坐标和列坐标的数位之和),这也是之后递归函数退出的条件之一 3. 递归的本质是系统帮压栈,所以非递归实现则是自己定义一个栈 stack 用来记录还没有被访问过的上下左右邻居,如果满足条件则压入栈 4. 每次弹出一个栈顶元素,直到栈为空
非递归解题代码:(java)
import java.util.Stack;
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
if(threshold<0 || rows<0 || cols<0)
return 0;
// 上下左右格子
int[] x = {-1,1,0,0};
int[] y = {0,0,-1,1};
boolean [] flags = new boolean[rows*cols];
flags[0] = true; // 访问过标记为 true
Stack<Integer> stack = new Stack<>();
stack.add(0);
int count = 0;
while (!stack.isEmpty()){
int curIndex = stack.pop();
count += 1;
for (int i = 0; i<4;i++){
int curRow = curIndex / cols + x[i];
int curCol = curIndex % cols + y[i];
int digitalSum = sum(curRow,curCol);
if (curCol>=0 && curCol<cols && curRow>=0 && curRow<rows && digitalSum <= threshold && !flags[curRow * cols + curCol]){
stack.add(curRow * cols + curCol);
flags[curRow * cols + curCol] = true;
}
}
}
return count;
}
private int sum(int i, int j){
int sum = 0;
do {
sum += i%10;
i = i/10;
}while (i>0);
do {
sum += j%10;
j = j/10;
}while (j>0);
return sum;
}
}