目录
前言
今天返校,没怎么学。昨晚看了链表的内容,发现我对于指针的使用还是太弱了,需要好好地学习学习。
一、单链表的定义
每个节点除了存放数据元素外,还要存储指向下一个节点的指针。
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针
二、单链表的基本操作
1、定义
用data来存储数据,并且用next来存指向下一个结点的指针。并为了以后使用方便,因此将其重命名,使用LNode时,表示定义的是一个结点;用LinkList的时候表示定义的是一个单链表。
typedef struct ListNode{
ElemType data;
struct ListNode *next;
}LNode, *LinkList;
2、初始化
1、无头结点方式
让单链表L的头指针直接指向NULL
bool InitList(LinkList &L){
L = NULL;
return true;
}
2、有头节点方式
首先用mallc函数申请一个单链表结点大小的空间,并将单链表L的头指针指向该空间,该空间就是一个头节点。头节点中data部分没有数据;只有一个next指针。随后将该next指针指向NULL,即可完成初始化。
bool InitList(LinkList &L){
L = (LNode*)malloc(sizeof(LNode));
if(L == NULL) return false;
L->next = NULL;
return true;
}
3、小结
使用带头节点的初始化方式,在后续使用时比较方便;不带头节点的方式在后续使用时比较麻烦。
3、增
1、按位序插入
由于是从前往后遍历,因此,只需要找到位序为 i-1 的节点,即可非常方便地在位序为 i 的地方插入节点。
//按位序插入,在单链表L位序为i的地方插入结点,其数据元素为elem
bool InsertByOrder(LinkList &L, int i, ElemType elem) {
if(i<1) return false; //位序不可小于1
LNode* p = L; //指针p指向L的头节点,表示当前扫描的节点
int j = 0; //j表示指针p指向节点的位序
while(p != NULL && j < i-1) { //扫描,找到位序i的前一位
p = p->next;
j++;
}
if(p == NULL) return false; //位序i-1为NULL,无法插入
LNode* temp = (LNode*)malloc(sizeof(LNode)); //定一个节点temp
if(temp == NULL) return false; //某些情况下可能创建失败
temp->data = elem;
temp->next = p->next; //节点temp的指针指向位序为i的节点
p->next = temp; //将位序为i-1的节点的next指针指向temp
return true;
}
2、在指定节点后插入(后插)
在仅知晓节点p的情况下,我们只知道p的next指针指向的节点,因此,后插是比较简单的。与按位序插入类似,只需要将temp节点的next指向p的next,再将p的next指向temp即可。
//在指定节点p后插入节点,其数据元素值为elem
bool InsertNextNode(LNode* p, ElemType elem) {
if(p == NULL) return false; //节点p本身并不存在
LNode* temp = (LNode*)malloc(sizeof(LNode));
if(temp == NULL) return false; //某些情况下可能创建失败
temp->data = elem;
temp->next = p->next;
p->next = temp;
return true;
}
3、在指定节点前插入(前插)
第一种方法是遍历至p节点,找到其前驱节点,在该节点后插入一个节点,就相当于在节点p前插入一个节点。但是我还没实现查找,而且时间复杂度较高,所以就记了第二种方法。
在仅知晓节点p的情况下,我们只知道p的next指针指向的节点,那么先在节点p的后面插入一个后继节点temp,随后将p的data赋给temp,那么这个temp节点就成p节点了(看上去是p,用起来也是p,还说你不是p?)随后将elem赋值给初始的那个p节点,这样就前插(夺舍)成功了。
//在指定节点p前插入节点,其数据元素为elem
bool InsertPreNode(LNode* p, ElemType elem) {
if(p == NULL) return false; //节点p本身并不存在
LNode* temp = (LNode*)malloc(sizeof(LNode));
if(temp == NULL) return false; //某些情况下可能创建失败
temp->data = p->data; //转移数据
temp->next = p->next;
p->data = elem;
p->next = temp;
return true;
}
4、删
今天还有事,等明天再复习。
总结
我对于指针的使用还是不够熟练。我虽然知道链表基本操作的原理,但是如何用代码实现还是比较薄弱,需要多做点题。
测试
int main() {
LinkList list;
if(InitList(list)) printf("work!\n");
LNode* p; //测试用节点
for(int i = 0; i<6; i++) {
ElemType temp = rand() % 10 + 1; //生成一个1~10的数
if(InsertByOrder(list, i+1, temp)) {
printf("数字%d插入成功!\n", temp);
} else {
printf("插入失败!\n");
}
}
p = list->next; //p指向第一个元素
while(p) {
printf("%d ", p->data);
p = p->next;
}
puts("");
p = list->next->next; //p指向第二个元素
if(InsertNextNode(p, 3)) { //后插元素3
printf("数字插入成功!\n");
} else {
printf("插入失败!\n");
}
if(InsertPreNode(p, 7)) { //前插元素7
printf("数字插入成功!\n");
} else {
printf("插入失败!\n");
}
p = list->next; //遍历
while(p) {
printf("%d ", p->data);
p = p->next;
}
return 0;
}