数据结构练习task
文章目录
堆栈
LeetCode题号: 20. 有效的括号
bool isValid(char * s){
if (s == NULL || s[0] == '\0') return true;
if(strlen(s)%2==1){
return false;
}
char *stack=(char*)malloc(sizeof(char)*(strlen(s)+1));
int top=0;
for(int i=0;i<strlen(s);i++){
if(s[i]=='('||s[i]=='['||s[i]=='{'){
stack[top++]=s[i];
}
else if(s[i]==')'||s[i]==']'||s[i]=='}'){
if((s[i]==')'&&stack[top-1]=='(')||(s[i]==']'&&stack[top-1]=='[')||(s[i]=='}'&&stack[top-1]=='{'))
top--;
else
return false;
}
else{
return false;
}
}
if(top==0){
free(stack);
return true;
}
else{
return false;
}
做题过程中出现的错误:stack[top-1]这种表达容易出现越界;
LeetCode题号:150. 逆波兰表达式求值
int evalRPN(char ** tokens, int tokensSize){
int *stack=(int*)malloc(sizeof(int)*(tokensSize));
int top=0;
for(int i=0;i<tokensSize;i++){
if(tokens[i][1] == '\0' && (tokens[i][0] == '+' || tokens[i][0] == '-' || tokens[i][0] == '*' || tokens[i][0] == '/')){
int num2=stack[--top],num1=stack[--top];
if(tokens[i][0]=='+'){
stack[top++]=num1+num2;
}
else if(tokens[i][0]=='-'){
stack[top++]=num1-num2;
}
else if(tokens[i][0]=='*'){
stack[top++]=num1*num2;
}
else if(tokens[i][0]=='/'){
stack[top++]=num1/num2;
}
}
else{
stack[top++] = atoi(tokens[i]);
}
}
int result=stack[0];
free(stack);
return result;
}
做题过程中出现的问题:
1.开始没有理解到char ** tokens是一个储存字符串的数组
2.没有考虑到输入负数的情况漏掉了tokens[i][1] == ‘\0’ 情况的讨论
LeetCode题号:42. 接雨水
int trap(int* height, int heightSize){
if(heightSize==0){
return 0;
}
int *stack=(int*)malloc(sizeof(int)*heightSize);
int top=0;
int water=0;
int tall=0,small=0;
stack[top]=height[top];
tall=top,small=top;
top++;
while(top<=heightSize-1){
stack[top]=height[top];
if(top==small&&stack[top]==stack[top-1]){
small=top;
tall=top;
top++;
continue;
}
else if(stack[top]<=stack[small]){
small=top;
}
else if(stack[top]>stack[small]&&stack[top]<stack[tall]){
small=top;
for(int i=tall+1;i<small;i++){
if(stack[small]>stack[i]){
water+=stack[small]-stack[i];
stack[i]=stack[small];
}
}
}
else if(stack[top]>=stack[tall]){
if(top-tall==1){
tall=top;
small=top;
}
else{
small=tall;
tall=top;
for(int i=small+1;i<tall;i++){
if(stack[small]>stack[i]){
water+=stack[small]-stack[i];
stack[i]=stack[small];
}
}
small=top;
}
}
top++;
}
return water;
}
做题过程中遇到的问题:
1.忽略输入空数组的情况
2.方法过于复杂
链表
LeetCode题号: 21. 合并两个有序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
struct ListNode *l3,*head ;
head = (struct ListNode*)malloc(sizeof(struct ListNode));
l3 = head;
while(l1&&l2){
if (l1 -> val < l2 -> val){
l3 -> next =l1;
l1 = l1 -> next;
l3 = l3 -> next;
}
else {
l3 -> next = l2;
l2 = l2 -> next;
l3 = l3 -> next;
}
}
l3 -> next = l1 ? l1 : l2;
return 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 个一组翻转链表(仍需进一步完善)
leecode上某位大神的题解(自己疯狂出错,就不放出来了)
//单组交换函数,p是该组的第一个的上一个
void swap(struct ListNode* p, int k) {
//单组翻转 思路:存所有有关的地址进数组,然后反向连接(从后向前指)
struct ListNode** arr;
struct ListNode* cur;
//开辟空间,注意需要多存 第一个的上一个和最后一个的下一个
arr = (struct ListNode**)malloc(sizeof(struct ListNode*)*(k + 2));
//存值,将地址存入数组
cur = p;
int i;
for (i = 0; i < k + 2; i++) {
//注意:cur有可能是NULL
arr[i] = cur;
if(cur!=NULL)cur = cur->next;
}
//头的上一个 指向 最后一个,即最后一个变成头
arr[0]->next = arr[k];
//从后向前反向连接
for (i = k; i >= 2; i--) {
arr[i]->next = arr[i - 1];
}
//原来的第一个指向原来的最后一个的下一个,即原来的第一个变成尾
arr[1]->next = arr[k + 1];
free(arr);//释放空间
}
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
if (k <= 1)return head;
struct ListNode* dummy;
struct ListNode* p;
struct ListNode* tmp;
dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->next = head;
//获取链表长度
int len = 0;
p = head;
while (p!=NULL) {
len++;
p = p->next;
}
p = dummy;
//k个一组翻转,需要几组
int ci = len / k;
while (ci--) {
swap(p, k);//单组翻转
if (ci != 0) {
//p定位到下一组的 第一个的上一个
int i;
for (i = k; i > 0; i--)p = p->next;
}
}
return dummy->next;
}
树
LeetCode题号:96. 不同的二叉搜索树(中等)
分析:
这道题本质上是一道递归的数学题,把每个节点都当作根节点来看,根据左右子树分配的节点总个数不同情况来计算,即f(n)=f(0)* f(n-1)+…+f(n-1) *f(0)
不过。。。时间复杂度高得一批。。。
int numTrees(int n) {
if(n==2){
return 2;
}
if(n==1){
return 1;
}
if(n==0){
return 1;
}
int t=0;
for(int i=0;i<=n-1;i++){
t+=numTrees(i)*numTrees(n-1-i);
}
return t;
}
LeetCode题号:101. 对称二叉树(简单)
思路一(无脑暴力求解):层序遍历树,遍历结果入栈,最上面两个相同就出栈,遇到NULL就赋成0。
可能由于我太菜了,没写出来能运行的解答。。。。
思路二(递归,借鉴了leecode大佬的思路):
如果一个树的左子树与右子树镜像对称,那么这个树是对称的。
因此,该问题可以转化为:两个树在什么情况下互为镜像?
如果同时满足下面的条件,两个树互为镜像:
1.它们的两个根结点具有相同的值。
2.每个树的右子树都与另一个树的左子树镜像对称。
上面的解释可以很自然地转换为一个递归函数
得到的结果还算不错的样子
bool isSymmetric(struct TreeNode* root){
if(root==NULL)
return true;
if((root->left==NULL&&root->right==NULL))
return true;
if(f(root->left,root->right))
return true;
else return false;
}
int f(struct TreeNode* root1,struct TreeNode* root2){
if(root1==NULL&&root2==NULL)
return 1;
if(root1!=NULL&&root2!=NULL&&root1->val==root2->val&&f(root1->left,root2->right)==1&&f(root2->left,root1->right)==1)
return 1;
else return 0;
}
LeetCode题号: 99. 恢复二叉搜索树(困难)(仍需进一步完善)
emm…我好菜啊,我连二叉搜索树都不知道是什么东西,题目都没读懂,看过leecode题解后稍微明白了一点点…先抄上再说
二分查找树定义如下:
1.若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2.若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3.任意节点的左、右子树也分别为二叉查找树;
4.没有键值相等的节点。
思路借鉴来源:某leecode大佬题解
思路一:
让我们来考虑交换的位置的可能:
1.根节点和左子树的某个数字交换 -> 由于根节点大于左子树中的所有数,所以交换后我们只要找左子树中最大的那个数,就是所交换的那个数
2.根节点和右子树的某个数字交换 -> 由于根节点小于右子树中的所有数,所以交换后我们只要在右子树中最小的那个数,就是所交换的那个数
3.左子树和右子树的两个数字交换 -> 找左子树中最大的数,右子树中最小的数,即对应两个交换的数
4.左子树中的两个数字交换
5.右子树中的两个数字交换
思路二:
如果记得 98 题,我们判断是否是一个合法的二分查找树是使用到了中序遍历。原因就是二分查找树的一个性质,左孩子小于根节点,根节点小于右孩子。所以做一次中序遍历,产生的序列就是从小到大排列的有序序列。
回到这道题,题目交换了两个数字,其实就是在有序序列中交换了两个数字。而我们只需要把它还原。
交换的位置的话就是两种情况。
1.相邻的两个数字交换
[ 1 2 3 4 5 ] 中 2 和 3 进行交换,[ 1 3 2 4 5 ],这样的话只产生一组逆序的数字(正常情况是从小到大排序,交换后产生了从大到小),3 2。
我们只需要遍历数组,找到后,把这一组的两个数字进行交换即可。
2.不相邻的两个数字交换
[ 1 2 3 4 5 ] 中 2 和 5 进行交换,[ 1 5 3 4 2 ],这样的话其实就是产生了两组逆序的数字对。5 3 和 4 2。
所以我们只需要遍历数组,然后找到这两组逆序对,然后把第一组前一个数字和第二组后一个数字进行交换即完成了还原。
所以在中序遍历中,只需要利用一个 pre 节点和当前节点比较,如果 pre 节点的值大于当前节点的值,那么就是我们要找的逆序的数字。分别用两个指针 first 和 second 保存即可。如果找到第二组逆序的数字,我们就把 second 更新为当前节点。最后把 first 和 second 两个的数字交换即可。
大神代码,先抄为敬
//刚才看了莫里斯遍历,线索二叉树的应用?先用迭代做吧,记录两个错误的结点,再交换值就行
void recoverTree(struct TreeNode* root){
//中序递归怎么找两个错误的结点是关键
struct TreeNode* stack[1000];int top = -1;
struct TreeNode *first,*second,*pre;
pre = NULL;first = NULL;
while(root!=NULL||top!=-1)
{
while(root!=NULL)
{
stack[++top] = root;
root = root->left;
}
root = stack[top--]; //出栈,现在是中序遍历的第一个结点
if(pre!=NULL&&first==NULL&&pre->val>root->val) first = pre; //记录第一个逆序对的前一个结点
if(pre!=NULL&&first!=NULL&&pre->val>root->val) second = root; //记录逆序对的第二个结点
pre = root;
root = root->right;
}
int temp = first->val;
first->val = second->val;
second->val = temp;
}