🍒20 包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个
能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路1:双栈法(辅助栈~n个变量)C++
class Solution {
public:
stack<int> m_data,m_min;//m_data 数据栈 m_min 辅助栈
void push(int value) {
m_data.push(value);
if( m_min.empty())
m_min.push(value);
else if(value <= m_min.top())//如果value 小于辅助栈栈顶的值,则辅助栈也要压入值,使得辅助栈的栈顶始终为min值
m_min.push(value);
}
void pop() {
if(m_data.top() == m_min.top())
m_min.pop();
m_data.pop();
}
int top() {
return m_data.top();
}
int min() {
return m_min.top();
}
};
思路1.2:双栈法(辅助栈~n个变量)Java
peek()函数返回栈顶的元素,但不弹出该栈顶元素。
pop()函数返回栈顶的元素,并且将该栈顶元素出栈。
import java.util.Stack;
public class Solution {
Stack<Integer> dataStack = new Stack<Integer>();
Stack<Integer> minStack = new Stack<Integer> ();
public void push(int value) {
dataStack.push(value);
if(minStack.isEmpty() || value <= minStack.peek())
minStack.push(value);
else
minStack.push(minStack.peek());
}
public void pop() {
dataStack.pop();
minStack.pop();
}
public int top() {
return dataStack.peek();
}
public int min() {
return minStack.peek();
}
}
🍒21 栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路1:辅助栈)C++
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {//两个数组
if(pushV.empty() || popV.empty() || pushV.size() != popV.size())
return false;
stack<int> s;//辅助栈
int j = 0;
for(int i = 0;i < pushV.size();++i){
s.push(pushV[i]);//原数列依次压入辅助栈
while(!s.empty() && s.top() == popV[j]){//辅助栈的栈顶和弹出栈的栈顶相同
s.pop();//满足判断条件弹出辅助栈的栈顶
++j;//确保比较的位置
}
}
if(s.empty())
return true;
return false;
}
};
思路1.2:辅助栈 复习vector知识 C++
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size() == 0) return false;
vector<int> stack;
for(int i = 0,j = 0;i < pushV.size();){
stack.push_back(pushV[i++]);
while(j < popV.size() && stack.back() == popV[j]){
stack.pop_back();
j++;
}
}
return stack.empty();
}
};
觉得这个大神的写法,可能是自己基础太差,但是觉得每一步真的都是在雕琢一件艺术品。
思路1.3:辅助栈 复习vector知识 C++
import java.util.ArrayList;
import java.util.Stack;//注意导入
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length == 0 || popA.length == 0)
return false;
Stack<Integer> s = new Stack<Integer>();
int popIndex = 0;//标识弹出序列的位置
for(int i = 0 ;i < pushA.length;i++){
s.push(pushA[i]);
while(!s.empty() && s.peek() == popA[popIndex]){
s.pop();
popIndex++;
}
}
return s.empty();//超喜爱这个写法
}
}
🍒22 从上往下打印二叉树(广度优先遍历二叉树BFS)
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路1:双端队列C++
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
vector<int> PrintFromTopToBottom(TreeNode* root) {
vector<int> result;//存放打印序列
if(root == NULL)
return result;
queue<TreeNode* > q;
q.push(root);
while(!q.empty()){
TreeNode* temp = q.front();//保存队列的头结点
q.pop();
result.push_back(temp -> val);
if(temp -> left != NULL)
q.push(temp -> left);
if(temp -> right != NULL)
q.push(temp -> right);
}
return result;
}
};
思路1.2:队列JAVA
Java和c++队列方面有些不同,我还是没有决定选择那个编程语言,可是以自己的能力,学精2个可能需要多努力。也可能不可行
import java.util.ArrayList;
import java.util.Queue;//注意导包
import java.util.LinkedList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> result = new ArrayList<Integer>();
if(root == null)
return result;
Queue<TreeNode> queue = new LinkedList<TreeNode>();//java中queue,list,map同级别接口,Linklist实现了queue接口
queue.offer(root);
while(!queue.isEmpty()){
TreeNode temp = queue.poll();//访问队头元素并移除
result.add(temp.val);
if(temp.left != null)
queue.offer(temp.left);//添加队尾元素
if(temp.right != null)
queue.offer(temp.right);
}
return result;
}
}
🍒23 二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路1:递归实现C++
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
//后序序列最后一个为树的根
//二叉搜索树特点:左子树的值小于根节点,右子树值比根节点大
vector<int> left,right;
if(sequence.empty()) return false;
int index = 0;//标记左右子树界限
int len = sequence.size();
int root = sequence[len - 1];//确定root
//遍历序列,找到比root小的左子树序列
int i;
for( i = 0;i < len;i++){
if(sequence[i] > root)
break;
}
//遍历根的右子树序列,检查是否有值小于root
for(int j = i;j < len;j++){
if(sequence[j] < root)
return false;
}
//判断左子树和右子树是否都为为二叉搜索树,递归实现
bool leftSub = true;
if(left.size())
leftSub = VerifySquenceOfBST(left);
bool rightSub = true;
if(right.size())
rightSub = VerifySquenceOfBST(right);
return leftSub && rightSub;
}
};
非递归
class Solution {
public:
bool VerifySquenceOfBST(vector<int> sequence) {
int len = sequence.size();
if(0 == len) return false;
int i = 0;
while(-- len){
while(sequence[i++] < sequence[len]);
// i++;
while(sequence[i++] > sequence[len]);
//i++;
if(i < len) return false;
i = 0;
}
return true;
}
};
思路1.2:递归实现JAVA
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence == null || sequence.length == 0){
return false;
}
return isBST(sequence,0,sequence.length - 1);
}
public static boolean isBST(int []sequence,int start,int root){
if(start >= root) return true;
int key = sequence[root];
int i = start;
for(;i < root; i++){
if(sequence[i] > key)
break;
}
for(int j = i;j < root;j++){
if(sequence[j] < key)
return false;
}
return isBST(sequence,start,i-1) && isBST(sequence,i,root - 1);
}
}
反思:感觉自己主动思考能力很差,很懒,做了有20几道了,自己独立思考出来基本都是大概思路,难独立编程,基础太差,还不勤学,你再这样真的会被时代,同龄人抛弃。
c++和Java一直在混着用,因为都是三角猫的功夫,难以抉择自己应专心研究哪个,最近的感受是Java有很多可以调用的函数实现需要的功能,耗时长。c++和c思路很多相似,尤其做算法题,但是很高效从时间上来看,做的久了,感觉c++在编程方面更易实现,较为好理解,得想想最近时间安排,复试要紧,作息最近在购物上耗费很多时间,人生主线还是要一切以为学习服务为主。
🍒24 二叉树中和为某一值的路径
题目描述
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
思路1:递归实现C++
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
void TreePath(TreeNode* root,int target,vector<int> &path,vector<vector<int> > &pathList){
if(root == NULL || root -> val > target)
return;
path.push_back(root -> val);
bool isLeaf = root -> left == NULL && root -> right == NULL;
if(root -> val == target && isLeaf){
pathList.push_back(path);
path.pop_back();
}
else{
TreePath(root -> left,target - root -> val,path,pathList);
TreePath(root -> right,target - root -> val,path,pathList);
path.pop_back();
}
}
public:
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
vector<vector<int> > FindPath;
vector<int> Path;
if(root == NULL)
return FindPath;
//std::vector<int> path;
//int currentSum = 0;
TreePath(root,expectNumber,Path,FindPath);
return FindPath;
}
};
思路1.2:递归实现JAVA
来自大神小结:
树类型的题目,一般有两种做法:递归的做法和非递归的做法。
递归的做法通常代码量少而且意图看起来非常清晰明确。
非递归的做法节约空间,但是写起来麻烦尤其是在临界的判断。
remove方法
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> path = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null )
return result;
path.add(root.val);
target -= root.val;
if(target == 0 && root.left == null && root.right == null)
result.add(new ArrayList<Integer>(path));
FindPath(root.left,target);
FindPath(root.right,target);
path.remove(path.size()-1);
return result;
}
}
思路2:回溯法,前序遍历JAVA
import java.util.*;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
private Stack<Integer> path = new Stack<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null)
return result;
find(root,target,0);
return result;
}
private void find(TreeNode root,int target,int cur){
if(cur == target && root == null){
result.add(new ArrayList(path));
return;
}
if(root == null)
return;
//path.push(root.val);
path.push(root.val);
find(root.left,target,cur + root.val);
path.pop();
if(root.left == null && root.right == null)
return;
path.push(root.val);
find(root.right,target,cur + root.val);
path.pop();
}
}
🍒25 复杂链表的复制
我知道为什么叫复杂了fo。。
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路1:分解问题 时间效率O(n) 最优解法 C++
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNode(pHead);
ConnectRandomNode(pHead);
return Distract(pHead);
}
//第一步:复制链表每一节点,并将复制的节点与原节点一一对应链接
void CloneNode(RandomListNode* pHead){
RandomListNode* pNode = pHead;
while(pNode != nullptr){
RandomListNode* pClonedNode = new RandomListNode(0);
pClonedNode -> label = pNode -> label;//复制节点
pClonedNode -> next = pNode -> next;
pClonedNode -> random = nullptr;
pNode -> next = pClonedNode;//使得复制的节点链接进原始链表
pNode = pClonedNode -> next;
}
}
//第二步:设置复制出来的节点的random
void ConnectRandomNode(RandomListNode* pHead){
RandomListNode* pNode = pHead;
while(pNode != nullptr){
RandomListNode* pClonedNode = pNode -> next;
if(pNode -> random != nullptr){
pClonedNode -> random = pNode -> random -> next;
}
pNode = pClonedNode -> next;
}
}
//第三步:长链表拆分
RandomListNode* Distract(RandomListNode* pHead){
RandomListNode* pNode = pHead;
RandomListNode* pClonedHead = nullptr;
RandomListNode* pClonedNode = nullptr;
if(pNode != nullptr){
pClonedNode = pNode -> next;
pClonedHead = pNode -> next;
pNode -> next = pClonedNode -> next;//A-> B 链接
pNode =pClonedNode -> next;//把B 赋给PNode;
}
while(pNode != nullptr){
pClonedNode -> next = pNode -> next;//A' -> B'链接
pClonedNode = pNode-> next;//把B'赋给pClonedNode
pNode -> next = pClonedNode -> next;//B -> C 链接
pNode = pClonedNode -> next;
}
return pClonedHead;
}
};
🍒26 二叉搜索树与双向链表?
觉得书上讲解以及知识点把握很精准,但是算法虽然最优,但是有些难懂,网友的思路有时灵活简便,但是目的就是解题,高效率,对于小白的自己很难把握到通过题目应巩固什么知识点,这道题的训练目的。
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路1:中序遍历,递归 C++
左子树最大的节点与根节点,右子树最小的节点相连接
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr)return nullptr;
TreeNode* pLastNodeList = nullptr;
ConvertNode(pRootOfTree,pLastNodeList);//pre
TreeNode* pHeadOfList = pRootOfTree;
while(pHeadOfList != nullptr && pHeadOfList -> left != nullptr){
pHeadOfList = pHeadOfList -> left;
}
return pHeadOfList;
}
void ConvertNode(TreeNode* pNode,TreeNode*& pLastNodeList){//保存指向上个节点,需用对指针的引用
if(pNode == nullptr){
return;
}
ConvertNode(pNode -> left,pLastNodeList);
pNode -> left = pLastNodeList;
if(pLastNodeList) pLastNodeList -> right = pNode;
pLastNodeList = pNode;
ConvertNode(pNode -> right,pLastNodeList);
}
};
🍒27 二叉搜索树与双向链表?
觉得书上讲解以及知识点把握很精准,但是算法虽然最优,但是有些难懂,网友的思路有时灵活简便,但是目的就是解题,高效率,对于小白的自己很难把握到通过题目应巩固什么知识点,这道题的训练目的。
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路1:中序遍历,递归 C++
🍒50 数组中重复的数字
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路1:比较交换C++
/*
1、判断输入数组有无元素非法
2、从头扫到尾,只要当前元素值与下标不同,就做一次判断,numbers[i]与numbers[numbers[i]],相等就认为找到了
重复元素,返回true,否则就交换两者,继续循环。直到最后还没找到认为没找到重复元素,返回false
*/
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(length <= 0 || numbers == NULL)
return false;
//判断元素是否非法
for(int i = 0;i < length;++i){
if(numbers[i] <= 0 || numbers[i] > length - 1)
return false;
}
for(int i = 0;i < length;++i){
while(numbers[i] != i){//当前元素和下标值不同,就做比较
if(numbers[i]== numbers[numbers[i]]){
*duplication = numbers[i];//记录重复数字
return true;
}
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
};
思路2:HashSet
HashSet对象中不能存储相同的数据,存储数据时是无序的。
HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的所以取数据也是按照哈希值取得。存储是无序和C++里的Set不一样,C++里面的Set是有序的
Java中set的contains方法
import java.util.*;
public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) {
Set<Integer> set = new HashSet<>();
for(int i = 0;i < length;i++){
if(set.contains(numbers[i])){
duplication[0] = numbers[i];
return true;
}
else{
set.add(numbers[i]);
}
}
return false;
}
}