线性表
1、线性表的定义 线性表简称为表,是由n(n>=0)个数据元素(也叫节点或表元素)组成的有限序列。n=0时,该线性表成为空表。 表中各数据元素: 1)存在线性关系 2)结构类型完全一致
2、线性表的特点
- 唯一首元素
- 唯一尾元素
- 除首元素外,任何一个元素都有一个前驱
- 除尾元素外,任何一个元素都有一个后继
- 每个元素有一个位序(下标)
线性表的顺序存储
1.线性表的顺序存储原理 用一组地址连续的存储单位按线性表元素之间的逻辑顺序,一次存储线性表的数据元素。(属于静态存储,不能自由扩充)
数据元素的逻辑顺序和物理上的存储顺序完全一致,因此不需要另外建立空间来记录各个元素间
的关系
2、优、缺点 优点:随机存储。可以随机存取表中的任意一个数据元素(查快) 缺点:存储密度大。在插入、删除某元素时,需要移动大量元素(改慢);浪费储存空间
代码实现
#include<stdio.h>
#include<stdlib.h>
#define INIT_SIZE 10
#define INCERMENT 5
typedef int ElemType;
typedef struct {
ElemType* elem;
int length;
int listsize;
}SqList;
int Init_Sqlist(SqList* L) {
L->elem = (ElemType*)malloc(INIT_SIZE * sizeof(ElemType));
if (!L->elem)
return -1;
L->length = 0;
L->listsize = INIT_SIZE;
return 0;
}
int Insert_SqList(SqList* L, int i, ElemType e) {
ElemType* newBase;
int j;
if (i<0 || i>L->length)
return -1;
if (L->length >= L->listsize) {
newBase = (ElemType*)realloc(L->elem , (L->listsize + INCERMENT) * sizeof(ElemType));
if (newBase == NULL)
return -1;
L->elem = newBase;
L->listsize += INCERMENT;
}
for (j = L->length - 1; j >= i; j--){
L->elem[j + 1] = L->elem[j];
}
L->elem[i] = e;
L->length++;
return 0;
}
int Find_SqList(SqList L, ElemType e) {
for (int i = 0; i < L.length ; i++) {
if (L.elem[i] == e)
return i + 1;
}
return 0;
}
int Delect_SqList(SqList* L, int i) {
if (i<0 || i>L->length)
return -1;
for (int j = i; j < L->length; j++)
L->elem[j] = L->elem[j + 1];
L->length--;
return 0;
}
void show(SqList L) {
for (int i = 0; i < L.length; i++) {
printf("%d\t", L.elem[i]);
}
printf("\n");
}
void main() {
SqList L;
Init_Sqlist(&L);
Insert_SqList(&L, 0, 1);
Insert_SqList(&L, 1, 2);
Insert_SqList(&L, 2, 3);
show(L);
int a = Find_SqList(L, 3);
printf("%d", a);
printf("\n");
Delect_SqList(&L, 1);
show(L);
system("pause");
}
线性表的链式存储
1.线性表的链式存储原理 用一组任意的存储单位来存放线性表的数据元素(存储单位可以是连续的,也可以是不连续的),数据元素之间的逻辑关系通过指针来指示。其中包括单链表、循环链表、双向链表。 2、优、缺点 优点:插入、删除通过修改链表指针,不需要移动数据元素(改快) 缺点:不可随机存取元素(查慢)
1、单链表
1)单链表存储原理 对于每一个数据元素来说,链表除了存放数据元素本身的值以外,还应一起存放数据元素的直接后继点所在存储单元的内存起始地址。
每个节点只有一个指向后继节点的指针。
每个节点包括数据域和指针域才可将数据元素之间的逻辑关系完全体现出来,(节点间的逻辑
结构比每个节点的实际内存地址显得更重要)
2) 头节点 定义:在链表头部加入一个特殊的节点,类型与数据节点相同,但其数据域不存放有效值,标识链表的头指针变量指向该节点。 作用:链表为空时头节点的头指针就指向头节点(头节点指针域为NULL);在插入和删除操作中无需进行特殊处理。
代码实现
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct LNode {
ElemType data;
struct LNode *next;
}LNode, *LinkList;
LNode *CreateNode(ElemType e) {
LNode *p = (LNode*)malloc(sizeof(LNode));
p->data = e;
p->next = NULL;
return p;
}
LinkList Init_LinkList() {
return CreateNode(0);
}
void Insert_LinkList(LinkList L, LNode* curNode, LNode* newNode) {
LNode* p = curNode;
if (!newNode) {
return;
}
if (!curNode) {
p = L;
while (p->next != NULL) {
p = p->next;
}
}
newNode->next = p->next;
p->next = newNode;
L->data++;
}
LNode* Find_LinkList(LinkList L, ElemType e) {
LNode* p = L->next;
int i = 0;
while (p != NULL) {
if (p->data == e)
return i;
p = p->next;
i++;
}
return NULL;
}
void Delete_LinkList(LinkList L, ElemType e) {
LNode* p, * c;
p = L;
while (p->next != NULL) {
if (p->next->data == e) {
c = p->next;
p->next = p->next->next;
L->data--;
free(c);
break;
}
p = p->next;
}
}
void show(LinkList L) {
LNode* p;
for (p = L->next; p != NULL; p = p->next) {
printf("%d\t", p->data);
}
printf("\n");
}
void main() {
LinkList L = NULL;
LNode* node1, * node2, * node3, * node4, * node5;
L = Init_LinkList();
if (L == NULL)
printf("链表初始化失败");
node1 = CreateNode(1);
node2 = CreateNode(2);
node3 = CreateNode(3);
node4 = CreateNode(4);
node5 = CreateNode(5);
Insert_LinkList(L, NULL, node1);
Insert_LinkList(L, NULL, node2);
Insert_LinkList(L, node1, node3);
Insert_LinkList(L, L, node4);
Insert_LinkList(L, node2, node5);
show(L);
int i = Find_LinkList(L, 3);
printf("%d", i);
printf("\n");
Delete_LinkList(L, 5);
show(L);
}
2、循环链表
将单链表的尾结点的指针域指向头节点。 特性:从任何一个节点开始,都可以遍历整个链表。(单链表:整个链表遍历需从头节点开始)
代码实现
LinkList Init_LinkList() {
LinkList head = CreateNode(0);
head->next = head;
return head;
}
void Insert_LinkList(LinkList head, LNode* xNode, LNode* newNode) {
LNode* tail;
LNode* p = head;
while(p->next != head) {
p = p->next;
}
tail = p;
if (xNode == NULL || xNode == tail) {
tail->next = newNode;
newNode->next = head;
}else {
newNode->next = xNode->next;
xNode->next = newNode;
}
head->data++;
}
3、双向链表
在链表节点中增加一个指向前驱节点的指针。 特性:可以快速找到节点前驱和后继 双向链表的储存结构描述
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode,* DuLinkList;
双向循环链表
双向链表中每个节点都有指向前驱和后继的指针,所以在插入、删除时对指针的操作必须在两个方向同时进行,否则可能会使其中一条链被截断(若截断可以利用另一条链来修补)。 因此,为了算法统一将双向链表的前向单链表和后向单链表共用一个头结点首尾相连,构成一个双向循环链表。
双向循环链表的某节点后插入
void inselem(DuLinkList L,ElemType e,DuLNode *p){
DuLNode *s,*r;
s = (DULNode*)malloc(sizeof(DuLNode));
s->data = e;
r = p->next;
s->next = r;
r->prior = s;
p->next = s;
s->prior = p;
}
在某节点前插入类似
双向循环链表的删除某节点
删除某节点,即是修改某节点前节点的next指针、后节点的prior指针,可以创建一个指针返回
删除的节点,最后释放删除节点空间(free())
|