链表的基本操作:
创建头,头插,尾插,增,删,改,查;
基本结构
结构体+主函数+子函数。
结构体:
链表的结构体分为数据域和指针域两部分,单链表的指针域只有一个指针,指向的是下一个节点,而双链表的指针域中存在两个指针,这两个指针域分别指向下一个节点和上一个结点。
typedef struct node {
int data;
node *next;
}Node, *pNode;
这里的node 和 Node地区别:
node:只是一个名字,跟学生管理系统中的student相同,用node是因为node的中文意思有节点。
Node:相当 struct node ,是为了方便后面的定义结点。
主函数:
主函数是程序必备的一部分,有关联表的题也不意外,在链表中每个部分之间区分的比较清楚,很多部分都是由子函数完成的,所以主函数中基本上只需要调用函数就可以了,相当于程序的骨干,控制函数的作用和走向。
int main()
{
pNode ha;
ha = InitList();
CreateList(ha);
panduan(ha);
return 0;
}
子函数:
链表的大多数功能的实现都是靠一个个独立子函数。
链表的特点:
1.链表可以将函数很好的利用,将链表的头传入子函数,子函数可以直接对链表进行操作,不需要再开辟很多内存,也不用考虑太多的关于返回值的问题。
2.链表在存储的时候明显优于数组,链表的每个节点的内存位置可以分布在内存区的各个位置,数组必须要一段连续的内存空间,而且,数组在开辟新的内存时很不方便,不像链表这样方便,开辟后可以很方便的连接在原来的链表上,一个链表的长度可以很长,不会出现超出界限的问题。
3.链表的信息访问时不是非常高效,时间复杂对会很高。
链表的创建
头插法:每次都将新的节点插到头的后面,直至整个链表信息全部保存完。
一般的流程是创建一个头,将头传到函数中,每次分配一个节点的内存空间,将信息放入到这个节点中,然后将这个节点放在头结点的后面,重点是要先将这个节点的next指向头的后面,然后将头的next等于这个节点(这个顺序不能改变,一旦改变就找不到原链表头结点后面的信息),头插法建立的链表中存储的信息顺序和输入时的顺序刚好是反的,所以在反转链表的是可以再用一遍头插法,就可以实现链表的反转。
下面的例子中是以某种结果的数据作为停止的头插法,我们还可以建立以若干个结点为循环的头插法,这时改变循环的次数就可以了
void toucha(LinkList head) {
Node *p, *r,*s;
int data;
p = head;
r = p->next;
printf("输入数据:\n");
while (1) {
scanf("%d", &data);
if (data == 0) { // 当输入的数据为0是停止头插法。
break;
}
s = (Node*)malloc(sizeof(Node));
s->data = data;
if (p->next == NULL) {
p ->next = s;
r = s;
} else {
s->next = r;
p->next = s;
r = s;
}
}
}
尾插法:尾插法的思想是将每一个新的结点都放到链表的最后,这样也可以建立一个链表,函数的基本结构肯定也是循环了,重点就是设置一个指针一直指向链表的尾部,然后建立一个新的结点,将新的结点变为新的链表结尾,尾插法建立的链表中的信息和输入时的信息是相同的。
void weicha (LinkList head) {
Node *p, *s;
int data;
p = head;
printf("输入数据:\n");
while (1) {
scanf("%d", &data);
if (data == 0) { //退出循环地条件
break;
}
s = (Node*)malloc(sizeof(node));
s->data = data;
p->next = s;
p = p->next;
}
p->next = NULL;
}
增加信息:选择增加的位置,然后遍历到需要增加的位置,接下来的操作和头插法的相同,必须是先将节点的next指向该插入位置的下一个节点,然后再将上一个结点的next指向插入的节点。
删除:遍历到需要删除的前一个节点的位置,然后将前一个节点的next等于需要删除的的节点的next;最后释放掉要删除节点的内存空间就可以了。
修改和查找删除有只有一点点的区别。