数据结构树和二叉树的学习使用&数据结构栈,队列,链表的学习使用
LeetCode题号:20. 有效的括号(简单)给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
思路:
1.初始化一个栈stack,定义一个指向栈顶元素的指针变量top
2.依次读取字符串内中的每一个字符;
3.遇到左括号‘(’,’[’,’{'则进栈,top+1;
遇到右括号,检查栈顶元素是否与之对应,若不对应,返回false;
若对应,则指向栈顶元素的指针top-1,指向栈内下一个元素。
直到读取完字符串,出现不匹配现象(返回 false)
读取完字符串后,栈内还有元素(返回false)
bool isValid(char * s){
if (s == NULL || s[0] == '\0') return true;//判断字符串s是否为空串
char *stack = (char*)malloc(strlen(s)+1); //为栈stack分配内存空间
int i,top=0;//用top指向栈顶元素
for (i = 0; s[i]; i++) {
if (s[i] == '(' || s[i] == '[' || s[i] == '{') stack[top++] = s[i];//若为左括号,则进栈
else {
if ((--top) < 0) return false;//判断栈中是否还有元素
if (s[i] == ')' && stack[top] != '(') return false;
if (s[i] == ']' && stack[top] != '[') return false;
if (s[i] == '}' && stack[top] != '{') return false;
}
}
if(top!=0) return false;//判断读取完字符串后栈中是否还有元素
free(stack);//释放内存空间
return(true);
}
LeetCode题号: 150. 逆波兰表达式求值(中等)根据逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
思路:
1.初始化一个栈stack,定义一个指向栈顶元素的变量top
2.将遇到的数字依次存入栈中若遇到的字符串为非数字,如+、-、*、/等,取出栈中的前两个元素num1,num2进行运算,并将结果再压入栈中。
int evalRPN(char ** tokens, int tokensSize){
int stack[10000], top = 0;
int num1, num2;
int i;
for(i = 0; i < tokensSize; i++)
{
if(tokens[i][1] == '\0'&& (tokens[i][0] == '+' || tokens[i][0] == '-' || tokens[i][0] == '*' || tokens[i][0] == '/') ){//若无tokens[i][1] == '\0',则读入负数时会使程序报错
num2 = stack[--top];
num1 = stack[--top];
switch(tokens[i][0]){//用switch_case语句确定不同的情况类型
case '-' :
stack[top++] = num1 - num2;
break;
case '+' :
stack[top++] = num1 + num2;
break;
case '*' :
stack[top++] = num1 * num2;
break;
case '/' :
stack[top++] = num1 / num2;
break;
default :
break;
}
}else{
stack[top++] = atoi(tokens[i]);//用atoi函数将字符串转化为数据输出,并存入栈中
}
}
return stack[--top];
}
LeetCode题号: 42.给定n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后最多能接多少雨水
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,可以接 6 个单位的雨水(蓝色部分表示雨水)
思路:
假设以第i个柱子为接水的起始边界,那么可以分为两种情况:
1.柱子 i 后面存在柱子 j ,其高度大于等于柱子 i,我们可以以height[i]为较低边界,在[i,j]之间接水。 那么柱子[i,j]之间可接水: (j - i - 1)* height[i] - sum(i,j) ,其中sum(i,j)表示(i,j)之间柱子的高度之和,不包括i,j。
2.柱子 i 后面不存在高度大于等于height[i]的柱子 ,并假设 i 后面的柱子的最大高度为 height[j] = t, 我们可以以height[i]为较高边界,在[i,j]之间接水。 那么柱子[i,j]之间可接水: (j - i - 1)* t - sum(i,j) 。
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if(n == 0) return 0;
int ans = 0;
int sum[n];
sum[0] = height[0];
for(int i=1;i<n;i++) sum[i] = sum[i-1] + height[i];
int maxd[n];
maxd[n-1] = height[n-1];
for(int i=n-2;i>=0;i--) maxd[i] = max(maxd[i+1],height[i]);
int i=0, tmp =0;
while(height[i] == 0) i++;
while(i < n-1)
{
if(maxd[i+1] >= height[i])
{
int j = i+1;
while(height[j] < height[i]) j++;
tmp = (j-i-1)*height[i];
tmp -= sum[j-1] - sum[i];
ans += tmp;
i = j;
}
else
{
int t = maxd[i+1];
int j = i+1;
while(height[j] < t) j++;
tmp = (j-i-1)*t;
tmp -= sum[j-1] - sum[i];
ans += tmp;
i = j;
}
}
return ans;
}
};
LeetCode题号: 21. 合并两个有序链表(简单)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode temp_head(0); //设置临时头节点temp_head
ListNode *ptr = &temp_head; //使用pre指针指向temp_head
while(l1&&l2){ //l1和l2不空时,对它们进行比较
if(l1->val < l2->val){ //如果l1对应的节点小于l2对应的节点
ptr->next = l1; //将ptr与较小的节点进行连接
l1 = l1->next;
}
else{
ptr->next = l2;
l2 = l2->next;
}
ptr = ptr->next; //ptr指向新连接的节点
}
if(l1){ //如果l1有剩余
ptr->next = l1; //将l1接到ptr后
}
if(l2){
ptr->next = l2; //将l2接到ptr后
}
return temp_head.next;
}
};
LeetCode题号: 2. 两数相加
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
//遍历两个链表,已得到二者各自的长度
struct ListNode*last1=l1;//定义last1以便于遍历l1(last指向最后一个节点)
int len1=0,len2=0,len;
for(;last1->next;len1++){
last1=last1->next;
}
struct ListNode*last2=l2;//定义last2以便于遍历l2(last指向最后一个节点)
for(;last2->next;len2++){
last2=last2->next;
}
//比较两个链表的长度,短的链表延长并用零补齐
if(len1<len2){
len=len2;
for(int i=0;i<len2-len1;i++){
struct ListNode*pn=(struct ListNode*)malloc(sizeof(struct ListNode));//定义指向新节点的pn,以创建新节点
pn->val=0;
pn->next=NULL;
last1->next=pn;//把新节点接到last1上去
last1=last1->next;
}
}
else if(len1>len2){
len=len1;
for(int i=0;i<len1-len2;i++){
struct ListNode*pn=(struct ListNode*)malloc(sizeof(struct ListNode));//定义指向新节点的pn,以创建新节点
pn->val=0;
pn->next=NULL;
last2->next=pn;//把新节点接到last2上去
last2=last2->next;
}
}
//两个链表对应节点分别求和,并将结果放到第三个链表对应节点处
struct ListNode*p3=(struct ListNode*)malloc(sizeof(struct ListNode));//新建用于储存结果的新链表p3
int mod=0;//定义决定进位的余数
p3->next=NULL;
p3->val=(l1->val+l2->val+mod)%10;
mod=(l1->val+l2->val+mod)/10;
struct ListNode*last3=p3;//定义指向尾部的指针
l1=l1->next;
l2=l2->next;
for(int i=0;i<len;i++){
struct ListNode*node=(struct ListNode*)malloc(sizeof(struct ListNode));//新节点
node->next=NULL;
node->val=(l1->val+l2->val+mod)%10;
mod=(l1->val+l2->val+mod)/10;
last3->next=node;//新节点加到新链表上
last3=last3->next;
l1=l1->next;
l2=l2->next;
}
return p3;
}
LeetCode题号: 25. K 个一组翻转链表 (困难)
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
借鉴思路
使用栈存储k个数据,每存储 st.size() == k ,则进行一次反转;
每次循环要记录返回的链表的最后一个节点 nextline , 为了链接下一个反转的首节点;
当st.size() < k 时,则将nextline->next = st 中的最下面的元素;
迭代法
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
int size = 0;
ListNode countNode = head;
while(countNode != null){
countNode = countNode.next;
size++;
}
if(size < k) return head;
ListNode curtail = null;
ListNode pre = head;
ListNode prelast = null;
ListNode cur, rear, first, last;
int count = 0;
while(count < size / k){
first = pre;
last = pre;
int index = 1;
cur = pre.next;
while(index < k){
rear = cur.next;
cur.next = pre;
last = cur;
pre = cur;
cur = rear;
index++;
}
if(curtail == null){
curtail = last;
}
count++;
if(count == size / k){
if(prelast == null){
first.next = cur;
}
else{
prelast.next = last;
first.next = cur;
}
}
else{
if(prelast != null){
prelast.next = last;
prelast = first;
pre = cur;
}
else{
prelast = first;
pre = cur;
}
}
}
return curtail;
}
}
递归法
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode prev = null;
ListNode cur = head;
ListNode next = null;
ListNode check = head;
int canProceed = 0;
int count = 0;
// 检查链表长度是否满足翻转
while (canProceed < k && check != null) {
check = check.next;
canProceed++;
}
// 满足条件,进行翻转(关键)
if (canProceed == k) {
while (count < k && cur != null) {
next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
count++;
}
if (next != null) {
// head 为链表翻转后的尾节点
head.next = reverseKGroup(next, k);
}
// prev 为链表翻转后的头结点
return prev;
} else {
// 不满住翻转条件,直接返回 head 即可
return head;
}
}
}
LeetCode题号:96. 不同的二叉搜索树(中等)
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
class Solution{
public List <TreeNote>generateTrees(int n)
{
if(n==0)
return new ArrayList<>();
return generateBST(1,n);
}
private List <TreeNode>generateBST(int left,int right)
{
List<TreeNode> res = new LinkedList<>();
if (left > right) {
// 划分不到的时填null.
res.add(null);
return res;
}
for (int i = left; i <= right; i++)
{
List<TreeNode> leftTrees = generateBST(left, i - 1);
List<TreeNode> rightTrees = generateBST(i + 1, right);
for (TreeNode leftTree : leftTrees)
{
for (TreeNode rightTree : rightTrees) {
// 每个循环都要构造新的节点,不能在for 循环外面生成.
TreeNode root = new TreeNode(i);
root.left = leftTree;
root.right = rightTree;
res.add(root);
}
}
}
return res;
}
}
LeetCode题号: 101. 对称二叉树(简单)
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1.左节点等于右节点
2.左节点的左子树等于右节点的右子树
3.左节点的右子树等有右节点的左子树
递归(害 没看懂)
class Solution {
public:
bool isSymmetric(TreeNode* root)
{
if(root==NULL)return 1;
return judge(root->left,root->right);
}
int judge(TreeNode * root1,TreeNode *root2)
{
if(!root1&&!root2)return 1;
else if(root1&&root2&&root1->val==root2->val&&judge(root1->left,root2->right)&&judge(root1->right,root2->left))return 1;
else return 0;
}
};
LeetCode题号: 99. 恢复二叉搜索树 (困难)
最后我想谈一些感想,对于链表我觉得蛮好用,可以解决很多问题,树我目前没有什么感想。感觉加入技术部的这后小一个月,分身乏术,没有太多的时间分配给技术任务的学习,可能是我效率太低,在班级事务,工图,微积分,C语言(张老师的实践训练好多TAT),信导的MATLAB之间周转,我很想每一样都兼顾,可是冥冥之中总有些东西抓也抓不住,我只能说尽力去做,不会的就找别人的CSDN看一看理解一下,能写就写,害。但期末可能task的质量不太高,毕竟学习为先