最近在看XMPP框架,里面的xml底层是用链表实现的,其实苹果官方的xml框架也是利用链表实现的!
所以这证明了,对于苹果开发者来说,了解数据结构还是非常有益处的!所以个人翻阅了下数据结构一书!
由于基础有限,所以有些地方可能会有些纰漏!下面是个人经验所得,如果错误,欢迎指正!
首先链表主要是通过结构体来实现的,通过将结构体内部成员设计为结构体本身的指针类型来实现传递链的效果!
例如:
struct node{
int element;
struct node *next;
};
这可以通过node->next来链接下一个结构体,下一个结构体也可以同样的方式链接另一个结构体,这样就形成了链的关系!
从描述中我们可以看出来,这对指针的需求非常高,所以如果大家对指针了解的不是太详细的话,可以复习下指针!
而通常我们为了可读性,可以采用下面的方法设计一个单链表
#include <stdio.h>
#include <stdlib.h>
#define Elemtype int
struct Node;
typedef struct Node *PtrNode;
typedef PtrNode Position;
typedef PtrNode List;
Position findPrevious(List L,Elemtype element);
struct Node{
Elemtype element;
Position next;
};
这样,在需要节点时,可以使用PtrNode做关键字,在需要获取具体位置(实际上也就是结构体指针)可以使用Position,如果要获取链表,可以通过List.
当然,个人习惯不同,也可以直接用struct Node!
然后我们需要创建一个链表.
需要说明的是,一般的操作习惯是为链表用节点设计一个表头,表头里不存有实际数据,只包含一个结构体指针!
表头的使用也是依个人习惯而定!但是<数据结构与算法分析>一书是推荐使用的!
// 创建链表
List createList(void){
// 首先分配一个表头的空间
List L = malloc(sizeof(struct Node));
// 判断分配空间是否成功,这是个好习惯!
if (L == NULL) {
printf("error");
return NULL;
}else{
L->next = NULL; // 初始化的表最好置空
L->element = 0;
return L;
}
}
需要注意的是,一定要在分配内存空间后进行判断,否则一旦因为内存满了(iPhone貌似很难出现这种情况)或其他问题,返回的就是一个NULL!然后就是创建后表头的指针和值清零的问题!
然后就是往链表中插入元素
// 插入元素
void insertElement(List L,Elemtype element){
// 要插入元素首先得新建一个节点,也就先得为节点开辟内存空间
PtrNode node = malloc(sizeof(struct Node));
if (node == NULL) {
printf("创建结点失败");
}else{
// 首先为节点赋值,我这里是插入到最后,如果插入数据过大,很耗时间,大家可以自己优化
node->element = element;
node->next = NULL;
// 首先取得链表的表头
Position P = L;
// 找到链表的最后位置
while (P->next != NULL) {
P = P->next;
}
// 将节点插入
P->next = node;
}
}
删除元素 }
// 从链表中删除节点
void deleteElement(List L,Elemtype element){
// 这里还是现获取表头,这样的话便于删除,否则直接获取节点的话,删除操作时还要找到上一个节点,Tmp是用于删除操作的
Position P = L,Tmp;
while (P != NULL) {
// 如果P的下一个节点不为空,且下一个节点的值为指定元素值时继续循环
// 也就是说P不是最后的节点,且P下一个节点就是我们要找的指定值的节点
if (P->next != NULL && P->next->element == element) {
// 先获取下一个节点
Tmp = P->next;
// 将下一个节点的下一个节点赋值给P的下一个节点的指针,也就是跳过P下一个节点,
P->next = Tmp->next;
// 然后将P下一个节点释放 这个过程很重要,忘了释放,或者顺序或者方法搞错了都会造成内存泄漏或者野指针问题
free(Tmp);
}else{
// 如果不是我们要找的节点,就去下一个节点
P = P->next;
}
}
}
删除元素特别需要注意的是删除时的操作,很多误操作最后会导致删除的结构体没有释放,或者丢失链表的问题!
这里我是删除链表中所有和指定值相同的值,书上给出的是删除第一个和指定元素相同的值,大家可以自己实现!
然后是寻找元素在链表中的位置
// 找到元素在链表中的位置
Position findElement(List L,Elemtype element){
// 首先链表必须不为空,也可以不判断,后续操作如果链表为空会返回NULL,但个人习惯,有输出也可以提醒调试
if (L->next == NULL) {
printf("链表为空.\n");
return NULL;
}
// 这里直接获取节点,因为只是要找元素位置而已
Position p = L->next;
while (p != NULL && p->element != element) {
p = p->next;
}
return p;
}
// 寻找上一个节点
Position findPrevious(List L,Elemtype element){
if (L->next == NULL) {
printf("链表为空\n");
return NULL;
}else{
Position P = L;
while (P->next != NULL && P->next->element != element) {
P = P->next;
}
return P;
}
}
其实链表的主要难点在于其结构在写代码时对于新手来说不够清晰明朗,如果刚刚尝试编写链表,可以手绘模型图,那样再观察相关操作,会有更直观的印象!
然后,其实后面的栈,队列,甚至是树结构的实现原理都是差不多的,主要是依据不同数据类型,而需求的操作不同而已!