主要是考完期中之后发现自己一些基础的东西(特指链表)太差了,所以去学了一下,现在把它挂上来,方便以后回忆
链表补救计划
1.malloc
malloc函数引用前需要#include
malloc函数的返回类型是void*,如果没有空间即返回失败,则返回0, 或者是NULL
free()把申请的空间还给系统,申请过的空间,最终都要还, 且只能还申请的空间的首地址,即申请的是什么就只能free()什么,容易出现的问题:
- free后又free了一次
- free的地址变了,即可能内存申请的时候用的是p,然后在程序执行时出现了p++,再去free时就会出现问题,建议先将p改为原来的值再去free
一个程序的例子: (输入一个数number,然后再连续输入number个数)
#include<stdio.h>#include<stdlib.h>int main(){int number, i;scanf("%d", &number);// for C99 and after// int a[number];// for mallocint *a;// 注意这里的sizeof后面是inta = (int*)malloc(number*sizeof(int));for (i=0; i<number; i++)scanf("%d", &a[i]);for (i=0; i<number; i++)printf("%d", a[i]);free(a);return 0;}
2.linked list
一个程序的例子:(输入n个数,n未知,以-1结尾,再输出)
#include<stdio.h>#include<stdlib.h>typedef struct _node{int value;struct _node* next;}node;int main(){node *head = NULL;int number;do{scanf("%d", &number);if (number == -1)break;node *p = (node*)malloc(sizeof(node)); //注意这里是sizeof(node)而不是sizeof(node*)p->value = number;p->next = NULL;if (head) //加这一步是为了确定head非空{node *tail = head;while (tail->next)tail = tail->next;tail->next = p;}elsehead = p;}while(1);node *q = head;for(; q; q=q->next){printf("%d", q->value);}return 0;}
3.linked list 函数
这里同样也是上一个的例子
#include<stdio.h>#include<stdlib.h>typedef struct _node{int value;struct _node* next;}node;typedef struct _list{node* head;}List;void build_linkedlist(List *plist);int main(){List list;list.head = NULL;int number;build_linkedlist(&list); //传进去的是list的地址node *q = list.head;for(; q; q=q->next){printf("%d", q->value);}return 0;}void build_linkedlist(List *plist){int number;do{scanf("%d", &number);if (number == -1)break;node *p = (node*)malloc(sizeof(node)); //注意这里是sizeof(node)而不是sizeof(node*)p->value = number;p->next = NULL;if ((*plist).head) //加这一步是为了确定head非空{node *tail = (*plist).head; //(*plist).head相当于plist->headwhile (tail->next)tail = tail->next;tail->next = p;}else(*plist).head = p;}while(1);return ;}
这里是通过建立了一个函数来对链表进行操作,建立结构体List的原因是方便对链表进行管理,成体系。
我们在向函数中传参数的时候传入的是list的地址,因为我们想对head这个指针做修改,即对一个东西的地址做修改,所以我们需要传入的是地址的地址,这样才能完成对其的修改。
4.其他一些要注意的地方
如果一个指针出现在了箭头的左边例p->value中的p,我们一定在程序中要直接或间接地判断p会不会是NULL
这点非常非常重要。
一般我们很有可能会没有考虑空链表的情况
例如,在上述代码中,p和q都出现在了箭头地左边,但p是被判断过地,因为在for循环地进入条件就是p即p!=NULL,而q是没有判断过的,所以为避免错误,这里应该补充对q的判断看其是不是NULL 这一点非常非常重要
删掉链表的结点时首先让其上一个结点的结点指向其下一个结点,再free掉该结点即可
删除链表
for (p=head; p; p=q){q = p->next;free(p);}
因为结束时p和q都是NULL所以就free完毕了
做题时遇到的问题
段错误
Polynomial Add( Polynomial a, Polynomial b ){if (a == NULL)return b;else if (b == NULL)return a;else{Polynomial head, tail;tail = (Polynomial)malloc(sizeof(struct Node));//这里如果让不对tail做初始化也会段错误,好像是因为一定要对指针做初始化,tail地初始化是malloc来的,而head的初始化是tail来的head = tail;//这里如果写head = tail = NULL;会段错误,好像是因为如果让其为NULL,它就没有Next了,所以会段错误a = a->Next;b = b->Next;while (a&&b){Polynomial this = (Polynomial)malloc(sizeof(struct Node));if (a->Exponent > b->Exponent){this->Exponent = a->Exponent;this->Coefficient = a->Coefficient;a = a->Next;}else if(a->Exponent < b->Exponent){this->Exponent = b->Exponent;this->Coefficient = b->Coefficient;b = b->Next;}else{this->Exponent = b->Exponent;this->Coefficient = b->Coefficient + a->Coefficient;a = a->Next;b = b->Next;if (this->Coefficient == 0)continue;}tail->Next = this;tail = this;}if (a)tail->Next = a;if (b)tail->Next = b;return head;}}
对于以上的代码,下面的写法也是可以通过的
Polynomial Add( Polynomial a, Polynomial b ){if (a->Next == NULL)return b;else if (b->Next == NULL)return a;else{Polynomial head, tail;head = tail = NULL;//这里让head和tail直接为NULLa = a->Next;b = b->Next;while (a&&b){Polynomial this = (Polynomial)malloc(sizeof(struct Node));if (a->Exponent > b->Exponent){this->Exponent = a->Exponent;this->Coefficient = a->Coefficient;a = a->Next;}else if(a->Exponent < b->Exponent){this->Exponent = b->Exponent;this->Coefficient = b->Coefficient;b = b->Next;}else{this->Exponent = b->Exponent;this->Coefficient = b->Coefficient + a->Coefficient;a = a->Next;b = b->Next;if (this->Coefficient == 0)continue;}if (head == NULL)head = this;//在第一次经过时,这里和下面分布重新对tail和head做了初始化elsetail->Next = this;tail = this;//}if (a)tail->Next = a;if (b)tail->Next = b;Polynomial head1 = (Polynomial)malloc(sizeof(struct Node));head1->Next = head;return head1;//head1的作用是为了加一个dummy head}}
而对没有dummy head的情况我们用下面的代码
head = tail = NULL;......if (head == NULL)head = cur;elsetail->next = cur;tail=cur;
但一般都是会有dummy head的,所以,嗯
给定一个有dummy head的链表,要求返回一个逆序的链表,且给定的链表也要是逆序的 (Data Structures and Algorithms (English) 6-4)
#include <stdio.h>#include <stdlib.h>typedef int ElementType;typedef struct Node *PtrToNode;typedef PtrToNode List;typedef PtrToNode Position;struct Node {ElementType Element;Position Next;};List Read(); /* details omitted */void Print( List L ); /* details omitted */List Reverse( List L );int main(){List L1, L2;L1 = Read();L2 = Reverse(L1);Print(L1);Print(L2);return 0;}/* Your function will be put here */List Reverse( List L ){if (L->Next==NULL || L->Next->Next==NULL)return L;List l1, pre, l2;l1 = L->Next->Next;l2 = L->Next;l2->Next = NULL;/*这里的上面三行我本来写成了{l1 = L->Next;l2 = L->Next;l2->Next = NULL;l1 = l1->Next;}但这样写是错的,因为我们不难看到在l1 = L->Next;l2 = L->Next;的赋值之后,l1和l2指向了同一块地方,而下一行的l2->Next = NULL;又对这一块地方做了修改,这就会使l1->Next也为NULL。*/while (l1){pre = l1->Next;l1->Next = l2;l2 = l1;l1 = pre;}L->Next = l2;return L;}
如果要使给定的链表也是逆序的就要让return 参数,即L
如上图所示,一个有dummy head的linked list,即L是一块地址,它指向一块地址,而这块地址的Next也是一块地址,指向链表的第一个结点,那么,我们往一个函数里传入L相当于传入了地址的地址,即在一个函数里,虽然主函数里的L和被调函数的L是位于不同的两个地址的两个相同的值,但正因为他们相同,都指向上图中中间这个结点,如果如果我们把这个结点的next修改一下,就可以改变原主函数中L所指向的值
Tree
做到的一些题
判断两个树是否同构
int Isomorphic( Tree T1, Tree T2 ){if (T1->Element == T2->Element){if (!T1->Left && !T1->Right && !T2->Left && !T2->Right)return 1;else if (!T1->Left && !T2->Left && T1->Right && T2->Right)...}elsereturn 0;}
不要像上面这么写,这么写的话会写很多,而且容易漏掉一些情况,要写成下面这种
int Isomorphic( Tree T1, Tree T2 ){if (T1 == NULL && T2 == NULL)return 1;else if (T1 != NULL && T2 == NULL)return 0;else if (T1 == NULL && T2 != NULL)return 0;else if (T1 != NULL && T2 != NULL){if (T1 -> Element != T2 -> Element)return 0;elsereturn ((Isomorphic(T1 -> Left, T2 -> Right) && Isomorphic(T1 -> Right, T2 -> Left)) || (Isomorphic(T1 -> Right, T2 -> Right) && Isomorphic(T1 -> Left, T2 -> Left)));}}
即传入的T1和T2可以是NULL然后让这个函数对是不是NULL来判断,这样写的话可以省好多位置
求二叉树的高度
用一个简单的递归即可
int countheight(BinTree T){if (T==NULL)return 0;else if (countheight(T->Left) > countheight(T->Right))return countheight(T->Left) + 1;elsereturn countheight(T->Right) + 1;}
