前言:之前学习链表的时候总会遇到一些问题
也看了好多人的文章感觉有些不是太实用
然后后来也是自己摸索才大概写出来的.
在真正的开发中会把链表的增删改查写到函数里
但是删除有点麻烦 找了很多都是删除第几个 而不是删除某个值对应的节点 让我很难受
所以想写一些链表的操作分享一些 我也不会用长一点的名字去命名 这样阅读性会好一些
不过在实际开发中建议使用较长的名字去命名好了 话不多说
一.链表的创建
一般分为头插法和尾插法
1.我在写项目的时候喜欢使用头插法
因为我希望在创建结点的时候按照某种方法去排序
如果用尾插法创建的话快 但是 如果在创建好后再排序就有些麻烦
话不多说贴代码.
第一步 让我们写出这个结点的声明`
typedef struct node {
int data;
struct node* next;
} Node;
然后先让我们看看主函数中该怎么写
#include <stdio.h>
#include <stdlib.h>
int main() {
Node *head;
head = NULL;
int n;//这里的n代表创建多少个结点
scanf("%d",&n);
for (int i = 0; i < n; ++i) {
int a;//这里的a代表传给结点的值
scanf("%d",&a);
insert(&head, a);//insert 增加链表结点函数
}
Node *temp = head;
while(temp != NULL) { //输出这个链表
printf("%d ",temp->data);
temp = temp->next;
}
return 0;
}
接下来就是inser()函数的写法了.
首先我们需要在这个函数里面创建一个结点
void insert(Node **head ,int value) {
//这个参数是取头结点的地址.
Node *temp = (Node*)malloc(sizeof(Node));
temp->next = NULL;
temp->data = value;
}
然后怎么利用头结点的头插法把这个链表连起来呢?
我们首先得判断 这个头结点是不是为NULL 因为在定义的时候定义了NULL
if((*head) == NULL) {
//这里*head 因为head是二级指针 加个*就降了一级.
(*head) = temp; //建议全部带括号*这个运算符的结合律防止出错都带括号把
}
其实这个判断也就是判断是不是第一次创建 头结点有没有东西
头结点要是有了东西呢?
else {
Node *t = (*head); //让临时指针的从头开始遍历.
while(t->next != NULL) { // 这里一定是t->next 如果让t连的话 就连接不起来
t = t->next;
}
t->next=temp; //指针域的作用就是连接.
}
好了 这就是头插法的创建
但是我们明显发现效率不高 尾插法创建一个就接到链表里面
创建一个就接进去.
但是头插法不行. 必须是创建一个 再从头开始.
但是!
看接下来的一个妙用 我要创建链表并且排序.
这里我们做一个约定 从小到大排列.让我们看看怎么做
还是像刚才创建一样 如果头是空怎么怎么样 否则怎么怎么样对吗?
想一想要改这里吗?
if((*head) == NULL) {
(*head) = temp;
} else {
答案肯定是不用改因为没数据肯定就把第一个放进去.
那么肯定就是改下一处了
按照我们刚在的想法是不是可以这样写
while(t != NULL) {
if(t->next->data > temp->data) { // 如果t的下一个的数据大于了新结点
temp->next = t->next; //让新结点的指针指向了t的下一个
t->next = temp; //再让t指向新结点
return; //返回就好
}
t = t->next;
}
但是这里其实是有两处问题的 先说第一处:
因为是从小到大比较的 我们上面的写法是从头开始
找到比他大的地方就让他插进去.但是如果新的结点就是最大的呢
也就是跑到完都没进到 if 语句中去
所以应该加一个
while(t != NULL) {
if(t->next == NULL) {
//这一次我们判断下一个是不是空
t->next = temp; //如果是空了连接一下
return; //返回就行
} else