数据结构学习笔记(一)—— 链表
(先在这里悄悄打个小广告 !^_^)
我看的是青岛大学王卓老师的数据结构视频,真的讲的非常好,每个点都讲得很仔细
B站链接:数据结构与算法基础(青岛大学-王卓)
链表
链表是一种很基础的数据结构,也可以利用链表构造其他的高级数据结构,掌握其中的一些思想也会方便学习之后的高级数据结构
一、基本结构与思想
传统的存储方式很容易想到用数组来实现。
用数组存储数据优点有很多比如:
- 创建起来比较方便
- 因为每个元素存储在连续的一片内存空间上,这样读取某个特定位置的元素就可以通过基地址+偏移量的方式很快的取出来
但是相应的缺点也是存在的
- 有时内存中没有大片的连续内存空间,就存不了较大规模的元素
- 要从数组中删除一个元素,就需要将其后面的元素一个一个向前搬移一格,会浪费大量的时间
因此就有了链表这样的一种思路,即每个元素不仅存储自身的信息,还存储一个指针,指向它的下一个元素,这样只要拿到了这个元素,就可以根据它的下一个元素指针找到下一个元素存放的内存位置。
最后一个元素之后没有元素了,所以将这个元素的下一个元素指针指向一个空值NULL
就像C语言声明数组一样,数组名表示了数组的首地址,链表也可以声明一个指针,指向链表的第一个元素,这样拿到这个指针就可以依次访问到数组中的各个元素了
拓展
在创建一个链表时可以把第一个节点作为头节点,在其中存储链表相关的信息。
二、C语言实现
1.链表结构体
每个链表元素我们称之为节点,每个节点包含两个成员,存储自身信息的数据域,存储下一个元素位置的指针域
typedef struct Lnode{
int val;
struct Lnode *next;
}Lnode, Linklist;
2.错误宏定义
宏定义内容 | 代表含义 | 值 |
---|---|---|
INDEX_OVERFLOW | 所给定位置超出链表范围 | -1 |
LINKLIST_EMPTY | 链表为空 | 400 |
#define INDEX_OVERFLOW -1
#define LINKLIST_EMPTY -400
3.链表相关方法
初始化链表
判断链表是否为空
在链表末尾添加元素
查找链表指定位置的值
查找指定值在链表中出现的位置, 返回第一次出现的下标
修改指定位置元素的值
在指定位置之后插入元素
删除指定位置元素
打印链表
1.初始化链表
将新建立的链表下一个元素指向NULL 我是使用将第一个元素节点作为头节点,在其中存储链表的长度这一信息/*
* 初始化链表
* @param {Linklist *L}
* @rerurn {void}
*/
void initLinkList(Linklist *L){
L->val = 0;
L->next = NULL;
return;
}
2.判断链表是否为空
/*
* 判断链表是否为空
* @param {Linklist *L}
* @rerurn {int (1-空 0-非空)}
*/
int isLinkListEmpty(Linklist *L){
if(L->next) return 0;
return 1;
}
3.在链表末尾添加元素
/*
* 在链表末尾添加元素
* @param {Linklist *L,
* int num}
* @rerurn {void}
*/
void addLinkListTail(Linklist *L,int num){
Lnode *newLnode = (Linklist *)malloc(sizeof(Lnode));
newLnode->val = num;
newLnode->next = NULL;
Linklist *p = L;
while(p->next){
p = p->next;
}
p->next = newLnode;
L->val++;
}
4.查找链表指定位置的值
/*
* 查找链表指定位置的值
* @param {Linklist *L,
* int pos,
* int *e}
* @rerurn {void}
*/
void searchLinkListIndexOf(Linklist *L,int pos,int *e){
int i=0;
Linklist *p = L;
if(!L->next) return;
while(p->next){
p = p->next;
if(i==pos){
*e = p->val;
return;
}
i++;
}
exit(INDEX_OVERFLOW);
}
5.查找指定值在链表中出现的位置, 返回第一次出现的下标
/*
* 查找指定值在链表中出现的位置,返回第一次出现的下标
* @param {Linklist *L,
* int num,
* int *pos}
* @rerurn {void}
*/
void searchLinkList(Linklist *L,int num,int *pos){
Linklist *p = L->next;
int i = 0;
while(p->next){
if(p->val==num){
*pos = i;
return;
}
i++;
p = p->next;
}
if(p->val==num){
*pos = i;
return;
}
*pos = -1;
}
6.修改指定位置元素的值
/*
* 修改指定位置元素的值
* @param {Linklist *L,
* int pos,
* int num}
* @rerurn {void}
*/
void modifyLinkListVal(Linklist *L,int pos,int num){
int i=0;
Linklist *p = L;
while(p->next){
p = p->next;
if(i==pos){
p->val = num;
break;
}
i++;
}
if(i==pos){
p->val = num;
}
return;
}
7.在指定位置之后插入元素
/*
* 在指定位置之后插入元素
* @param {Linklist *L,
* int pos,
* int num}
* @rerurn {void}
*/
void insertLinkLisAtIndex(Linklist *L,int pos,int num){
if(pos==L->val-1){
addLinkListTail(L,num);
return;
}
Lnode *newNode = (Lnode *)malloc(sizeof(Lnode));
Linklist *p = L->next,*q = NULL;
newNode->val = num;
int i = 0;
while(p->next){
if(i==pos){
q = p->next;
newNode->next = q;
p->next = newNode;
L->val++;
return;
}
p = p->next;
i++;
}
exit(INDEX_OVERFLOW);
}
8.删除指定位置元素
/*
* 删除指定位置元素
* @param {Linklist *L,
* int pos}
* @rerurn {void}
*/
void removeLinkListAtIndex(Linklist *L,int pos){
if(isLinkListEmpty(L)) exit(LINKLIST_EMPTY);
if(pos<0 || pos>L->val-1) exit(INDEX_OVERFLOW);
Linklist *p = L->next,*q = NULL;
if(pos==0){
L->next = L->next->next;
L->val--;
free(p);
return;
}
if(pos==L->val-1){
while(p->next){
q = p;
p = p->next;
}
q->next = NULL;
free(p);
L->val--;
return;
}
int i = 0;
while(p->next){
if(i==pos){
q->next = p->next;
L->val--;
free(p);
return;
}
i++;
q = p;
p = p->next;
}
exit(INDEX_OVERFLOW);
}
9.打印链表
其实这是一个遍历链表的方法,可以传入一个回调函数,用于对每个遍历到的元素执行一定的操作,但是我这里没有写,有机会再完善吧!
/*
* 打印链表 eg: val1->val2->val3->NULL
* @param {Linklist *L}
* @rerurn {void}
*/
void printLinkList(Linklist *L){
Linklist *p = L;
if(!p->next){
printf("NULL\n");
return;
}
while(p->next){
p = p->next;
printf("%d->",p->val);
}
printf("NULL\n");
return;
}
三.测试
int main(void){
Linklist L;
int a;
initLinkList(&L);
addLinkListTail(&L,1);
addLinkListTail(&L,2);
addLinkListTail(&L,3);
addLinkListTail(&L,4);
addLinkListTail(&L,5);
addLinkListTail(&L,6);
addLinkListTail(&L,7);
addLinkListTail(&L,8);
addLinkListTail(&L,9);
printLinkList(&L);
printf("length: %d\n",L.val);
modifyLinkListVal(&L,4,69);
printLinkList(&L);
searchLinkListIndexOf(&L,1,&a);
insertLinkLisAtIndex(&L,2,38);
printLinkList(&L);
printf("length: %d\n",L.val);
removeLinkListAtIndex(&L,8);
printLinkList(&L);
removeLinkListAtIndex(&L,0);
printLinkList(&L);
printf("length: %d\n",L.val);
searchLinkList(&L,3,&a);
printf("Index is:%d\n",a);
printLinkList(&L);
return 0;
}