线性表
顺序结构
结构定义
1
2
3
4
5
6
7
typedef struct SeqList {
ElemType elem[MAXSIZE]; //线性表占用的数组空间
int last; //记录线性表中最后一个元素在数组elem[]中的位置(下标值),空表置为-1
} SeqList, LList, *LListPtr;查找
按序号查找
按内容查找
- 遍历数组元素
- 找到与所找元素值相同的元素
- 返回元素位置
1
2
3
4
5
6
7
8
9
10
11
12
13int Locate( SeqList L,ElemType e) {
/*在顺序表L中查找与e相等的元素,若L.elem[i] = e,则找到该元素,并返回i + 1,若找不到,则返回-1*/
i = 0; /*i为扫描计数器,初值为0,即从第一个元素开始比较*/
while((i <= L.last) && (L.elem[i] != e)) {/*顺序扫描表,直到找到值为e的元素,或扫描到表尾而没找到*/
i++;
}
if(i <= L.last) {
return(i + 1); /*若找到值为e的元素,则返回其序号*/
} else {
return(-1); /*若没找到,则返回空序号*/
}
}
插入
- 找到要插入的位置
- 将该位置后的所有元素依次后移
- 将元素插入
- 更新数组长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int lnsList(SeqList* L, int i, ElemType e) {
/*在顺序表L中第i个数据元素之前插入一个元素e。i的合法取值范围是1 ≤ i ≤ L->last + 2*/
int k;
if((i < 1) || (i > L->last + 2)) { /*首先判断插入位置是否合法*/
printf("插入位置i值不合法");
return(ERROR);
}
if(L->last >= MAXSIZE - 1) {
printf("表已满,无法插入");
return(ERROR);
}
for(k = L->last; k >= i - 1; k--) { /*为插入元素而移动位置*/
L->elem[k + 1] = L->elem[k];
}
L->elem[i - 1] = e; /*在C语言数组中,第i个元素的下标为i-1*/
L->last++;
return(OK);
}删除
- 找到要插入的位置
- 将该位置后的所有元素依次前移
- 更新数组长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16int DelList(SeqList * L,int i,ElemType * e) {
/*在顺序表L中删除第i个数据元素,并用指针参数e返回其值。i的合法取值为1 ≤ i ≤ L.last + 1*/
int k;
if((i < 1) || (i > L->last + 1)){
printf("删除位置不合法!");
return(ERROR);
}
*e = L->elem[i - 1]; /*将删除的元素存放到e所指向的变量中*/
for(k = i; i <= L->last; k++) {
L->elem[k - 1] = L->elem[k]; /*将后面的元素依次前移*/
}
L->last--;
Return( OK);
}
链式结构
单链表(带头结点)
结构定义
1
2
3
4typedef struct _node {
ElemType data;
struct _node *next;
} Node, *NodePtr, LList, *LListPtr;初始化
1
2
3
4
5void InitList(LListPtr *L) {
//生成头结点,并将头结点的指针域置为空
*L = (NodePtr)malloc(sizeof(Node));
(*L)->next = NULL;
}查找(简单遍历)
插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23bool InsList(LListPtr L, int i, ElemType e) {
NodePtr p = L;
int k = 1;
//从头开始移动工作指针,同时计数,数到i-1,即找到要插入位置的前一个位置
for (; p != NULL && k < i; ++k) {
p = p->next;
}
//如果遍历完了链表,还没有数到i-1,则说明插入位置不合理
if (p == NULL) {
return false;
}
//生成新节点并插入到结点p之后
NodePtr q = (NodePtr)malloc(sizeof(Node));
q->data = e;
q->next = p->next;
p->next = q;
return true;
}删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20bool DelList(LListPtr L, int i, ElemType *e) {
NodePtr p = L;
int k = 1;
for (; p->next != NULL && k < i; ++k) {
p = p->next;
}
//如果遍历完了链表,还没有数到i-1,则说明删除位置不合理
if (p->next == NULL) {
return false;
}
NodePtr q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return true;
}
循环链表
结构定义
1
2
3
4typedef struct _node {
ElemType data;
struct _node *next;
} Node, *NodePtr, LList, *LListPtr;初始化
1
2
3
4void InitList(LListPtr *L) {
(*L) = (NodePtr)malloc(sizeof(Node));
(*L)->next = *L; //头结点的指针域指向头节点为空
}插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79bool InsList(LListPtr L, int i, ElemType e) {
NodePtr p = L; //TODO
int k = 1; //TODO
//TODO
for (; p->next != L && k < i; ++k)
p = p->next;
if (p->next == L && k < i) return false;
NodePtr q = (NodePtr)malloc(sizeof(Node));
q->data = e;
q->next = p->next;
p->next = q;
return true;
}
/*
* 这里解释一下循环的条件判断(不考虑i<1)
* 1. 假设链表里有5个节点。图中,L代表头结点,数字表示节点的数据域,------>表示指针域。^表示下方工作指针的指向。
* 1) 如果i=1(边缘条件,但却是可接受位置)
* i=1
* L------>1------>2------>3------>4------>5------>L
* ^ ^
* p p->next
* k=1
* p->next!=L成立,但k<i(即1<1)不成立,循环结束。
* 在后续的那条if (p->next == L && k < i)中,p->next==L不成立,因此不执行返回false。p停在了节点L上,这是正确的插入位置。
*
* 2) 如果i=3(1<i<=5,在正常范围)
* i=3
* L------>1------>2------>3------>4------>5------>L
* ^ ^
* p p->next
* k=1
* 那么进行第一次循环判断时,两个条件都会成立,循环继续。当进行到这里时:
* i=3
* L------>1------>2------>3------>4------>5------>L
* ^ ^
* p p->next
* k=3
* p->next!=L成立, 但k<i(即3<3)不成立,循环结束。
* 在后续的那条if (p->next == L && k < i)中,p->next==L不成立,因此不执行返回false。p停在节点2上。这是正确的插入位置。
*
* 3) 如果i=6。链表里有5个节点,所以这个插入位置是可以接受的,即表示新节点插在5节点之后,L节点“之前”。
* 那么当进行到这里时:
* i=6
* L------>1------>2------>3------>4------>5------>L
* ^ ^
* p p->next
* k=6
* p->next!=L不成立,循环结束。
* 在后续的那条if (p->next == L && k < i)中,p->next==L成立,但k<i(即6<6)不成立,因此不执行返回false,p停在了节点5上,这是正确的插入位置。
*
* 4) 如果i>6,假设为7,超出节点数量。
* 参考3),当p->next==L成立时,循环结束。此时,k=6,没有计数到7,即此时k<i也成立。这就是后面“插入位置不合理”判断的依据。
*
* 2. 极端情况:链表为空时
* 1) 如果i=1,插入位置是可接受的。
* i=1
* L------>L
* ^ ^
* p p->next
* k=1
* p->next!=L不成立,循环结束。
* 在后续的那条if (p->next == L && k < i)中,p->next==L成立,但k<i(即1<1)不成立,因此不执行返回false,p停在了节点L上,这是正确的插入位置。
*
* 2) 如果i=任意>1的数,例如2
* i=2
* L------>L
* ^ ^
* p p->next
* k=1
* p->next!=L不成立,循环结束。
* 在后续的那条if (p->next == L && k < i)中,p->next==L成立,k<i(即1<2)也成立,因此执行返回false。
*/删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15bool DelList(LListPtr L, int i, ElemType *e) {
NodePtr p = L;
int k = 1;
for (; p->next != L && k < i; ++k)
p = p->next;
if (p->next == L) return false;
NodePtr q = p->next;
p->next = q->next;
free(q);
return true;
}
双向链表
结构定义
1
2
3
4
5typedef struct _node
{
ElemType data;
struct _node *next, *prior;
} Node, *NodePtr, LList, *LListPtr;初始化
1
2
3
4void InitList(LListPtr *L) {
(*L) = (NodePtr)malloc(sizeof(Node)); //生成头结点
(*L)->prior = (*L)->next = NULL; //头结点的指针域为空
}插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27bool InsList(LListPtr L, int i, ElemType e) {
NodePtr p = L;
int k = 1;
while (p != NULL && k < i) {
p = p->next;
++k;
}
if (p == NULL) return false;
NodePtr s = (NodePtr)malloc(sizeof(Node));
s->data = e;
if (p->next == NULL) { //p指向最后一个结点
s->next = NULL;
s->prior = p;
p->next = s;
} else {
s->next = p->next;
s->prior = p;
p->next->prior = s;
p->next = s;
}
return true;
}删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21bool DelList(LListPtr L, int i, ElemType *e) {
NodePtr p = L;
int k = 1;
while (p != NULL && k < i) {
p = p->next;
++k;
}
if (p == NULL || p->next == NULL) return false;
//TODO
p = p->next;
p->next->prior = p->prior;
p->prior->next = p->next;
free(p);
return true;
}