上一篇用数组实现了线性表的存储,但是这种方式在插入和删除元素时,需要通过元素的移动来实现,效率比较低。而且在估计的最大数组个数小于实际情况时,不好处理。我们可以通过链式存储来实现,是通过“链”来连接元素,因此对于线性表的插入和删除只需要对链进行操作即可。
我们将链表中的一个节点表示为如下(每个节点都包含一个指向下一个节点的指针):
typedef int ElementType; //定义元素类型
typedef struct LNode * pToLnode; //定义一个结构体指针
struct LNode {
ElementType Data; //表示当前节点存储的元素
pToLnode next; //指向下一个节点
};
我们用List来表示这个链表的表头节点指针:
typedef pToLnode List;
//List L; 代表了一个链表
typedef pToLnode Position; //表示指向某个节点的指针
下面来看一下这个链表的相关操作:
链表的长度:
如果是数组方式的线性表,可以直接返回last+1表示长度,但是在链式表中,只能通过遍历链表实现查询长度。
int getLength(List L){ //求表长
Position p = L;
int length = 0;
while(p){
p = p->next;
length++;
}
return length;
}
按序号查找:
ElementType FindWithIndex(List L,int index){ //按下标查找
Position p = L;
int len = 1;
while(p && len < index){//遍历链表 当遍历结束 或 len==下标时跳出
p = p->next;
len++;
}
if (len == index && p) return p->Data;//返回找到的元素
else return -1; //返回-1表示该位置不存在
}
按值查找:
Position FindWithValue(List L,ElementType x){//按值查找
Position p = L;
int len = 1;
while(p && p->Data != x){//遍历链表 直到 遍历结束 或 找到值
p = p->next;
len++;
}
if (p->Data == x && p) return p;
else return NULL; //返回null表示查找失败
}
插入元素到表中:
List insertList(List L, ElementType x, int n){//插表
Position temp, pre;
temp = (Position)malloc(sizeof(struct LNode));
temp->Data = x;
if (x == 1){ //插到表头
temp->next = L; //插到表头的时候需要对L重新赋值L = insert(...) 因为表头位置已经改变
return temp;
}else{
int len = 0;
pre = L;
while(pre && lennext;
len++;
}
if (pre == NULL && len != n){
printf("没有该位置\n");
free(temp);
return NULL;
}else{
temp->next = pre->next;
pre->next = temp;
return L;
}
}
}
这样的情况下,需要区分是否插入到表头的情况。而且这种方式,接口也和之前的不一样了。我们还可以改进一下,为链表增加一个空的“表头节点”,真正的第一个表头元素在这个节点之后,这样处理表头就和处理其他位置的元素一样了,表头节点永远指向一个固定的位置。
//假设链表已经有了一个头结点
bool insertList1(List L,ElementType x, int n){//带头结点的表的插入
Position pre,temp;
pre = L;
int len=0;
while(pre && lennext;
len++;
}
if (pre == NULL || len != n){
printf("位置错误\n");
return false;
}else{
temp = (Position)malloc(sizeof(struct LNode));
temp->Data = x;
temp->next = pre->next;
pre->next = temp;
return true;
}
}
链表元素的删除:
bool deleteList(List L, int index){ //带头结点的链表的删除
Position pre, temp;
pre = L;
int len = 0;
while(pre && lennext;
len++;
}
if (pre == NULL || len != index-1 || pre->next == NULL){
printf("位置错误\n");
return false;
}else{
temp = pre->next;
pre->next = temp->next;
free(temp);
return true;
}
}
总结:
1.在单链表上插入、删除一个节点时,需要知道这个节点之前的一个节点(前驱节点);
2.单链表不能直接通过需要访问,只能从头指针一个一个按顺序访问;