1、顺序存储结构(如数组)
定义:
#define MAXSIZE 20
typedef struct{
int data[MAXSIZE]; //假设这里是整型
int length; //线性表长度
};
读取其中某个元素:假设线性顺序表已存在,读取其中第i个元素,其值返回到e中
#define ERROR 0
#define OK 1 //定义返回状态
int GetElem(Squlist L, int i, int *e){
if (L.length == 0 || i<1 || i>L.length) //该元素不存在或者超出表范围或者表为空则读取失败
return ERROR;
*e = L.data[i - 1];
return OK; //读取成功
};
插入操作:在第i个元素前面插入元素e
int ListInsert(Squlist *L, int i, int e){
int k;
if (L->length == MAXSIZE) //表格是否满
return ERROR;
if (i<1 ||i>length+1) //插入i不在范围内
return ERROR;
if (i <= L->length)
{
for (k = length - 1; k >= i - 1; k--)
L->data[k + 1] = L->data[k];
}
L->data[i] = e;
length++;
return OK;
};
删除操作:删除第i个元素并返回其值e
int ListDelete(Squlist *L, int i, int *e){
int k;
if (L->length == 0) //表格为空
return ERROR;
if (i<1 || i>L->length) //删除位置不正确
return ERROR;
if (i <= L->length){
*e = L->data[i - 1];
for (k = i; k < L->length - 1;k++)
L->data[i - 1] = L->data[i];
L->length--;
return OK;
}
}
总结特点:查询元素方便(时间复杂度O(1)),插入和删除元素复杂(时间复杂度O(n))
typedef struct Node { //定义链表中的节点
int data; //假设数据为整型
struct Node *next;
};
typedef struct Node *Linklist; //定义一个链表类型的指针
链表读取:读取第i个数,其值返回e中
#define ERROR 0;
#define OK 1;
int GetElem(Linklist L, int i, int *e){
int j=1;
Linklist p;
p = L->next; //表示p指向了L->next,理解这点很重要
while (p && j < 1){
p = p->next;
j++;
}
if (!p || j > i)
return ERROR;
&e = p->data;
return OK;
}
链表整表创建:头插法
void CreatLinklist(Linklist L, int n){
Linklist p;
int i;
srand(time(0)); //初始化随机数种子
L = (Linklist)malloc(sizeof(Node));
L->next = NULL; //建立带头结点的链表
for (i = 0; i < n; i++){
p = (Linklist)malloc(sizeof(Node));
p->data = rand() % 100 + 1;//随机生成100以内的数字
p->next = L->next;
(->next = p; //插入到表头
}
}
链表的插入:在第i个元素前面插入元素e
int LinklistInsert(LinkList L, int i, int e){
int j;
Linklist s, p;
p = L;
while (p && j < i){ //先找到插入的位置
p = p->next;
j++;
}
if (!p || j > i)
return ERROR;
s = (Linklist)malloc(sizeof(Node));
s->data = e;
s->next = p->next; //插入链表s到p的后继
p->next = s;
return OK;
}
链表的删除:删除第i个元素,返回其值到e中
int LinklistDelete(Linklist L, inti, int *e){
int j = 1;
Linklist p, q;
p = L;
while (p && j < i){
p = p->next;
j++;
}
if (!(p->next) || j > i)
return ERROR;
q = p->next;
p->next = q->next; //将q的后继赋值给p
*e = q->data;
free(q); //系统回收此节点,释放内存
return OK;
}
整个链表的删除:
int CleatLinklist(Linklist L){
Linklist p, q;
p = L->next;
while (p){
q = p->next;
free(q);
p=q
}
L->next = NULL; //头结点指针域为空
return OK;
}
3、循环链表
把单链表的终端节点指针域指向头结点,就形成了循环链表,可方便进行链表全部结点的遍历。判断的方法就是看p->next是否为头结点来结束循环
4、双向链表
和单向链表区别仅在于多了一个prior指针指向前面的结点,因此插入和删除操作也就多了几个关于prior的步骤
定义
typedef struct Node { //定义链表中的节点
int data; //假设数据为整型
struct Node *next;
struct Node *prior;
};
单链表翻转
思路是每次翻转链表,把原始链表的第一个元素后面的那个元素放到表头
LinkList ReverseLinkedList(LinkList L)
{
Node *first = NULL; //指向原始链表最开始的第一个数据
Node *behind = NULL; //每次新生成链表时,指向原始链表最开始的第一个数据的下一个数据
if (L == NULL)
{
return NULL;
}
first = L->next;
while (first->next != NULL)
{
behind = first->next;
first->next = behind->next;
behind->next = L->next;
L->next = behind;
}
return L;
}