Appearance
经典抽象数据类型
有些抽象数据类型(ADT)是不可或缺的工具,链表,堆栈, 队列, 树等
内存分配
所有的ADT都必须确定一件事情,如何获取内存来存储值
- 静态数组: 固定的长度,编译的时候确定
- 动态分配数组: 运行时候决定
- 动态分配链式结构: 最灵活
堆栈
接口
传统的堆栈有三种接口, push, top, pop
top: 查看顶层
push: 入栈
pop: 移出但是不返回
还需要一个判断栈是不是为空的函数, 以及一个判断是不是满了的函数
实现
stack.h
C
1 #define STACK_TYPE int
2 #include <stddef.h>
3 void push(STACK_TYPE value);
4 STACK_TYPE pop(void);
5 STACK_TYPE top(void);
6 int is_empty(void);
7 int is_full(void);
8 void create_stack(size_t size);
9 void destory_stack(void);
stack.c
C
1 #include "stack.h"
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <assert.h>
5
6 static size_t stack_size;
7 static STACK_TYPE *stack;
8 static int top_element = -1;
9
10 void create_stack(size_t size)
11 {
12 assert(stack_size == 0);
13 stack_size = size;
14 stack = malloc(stack_size * sizeof(STACK_TYPE));
15 assert(stack != NULL);
16 }
17
18 void destory_stack(void)
19 {
20 assert(stack_size > 0);
21 stack_size = 0;
22 free(stack);
23 stack = NULL;
24 }
25
26 void push(STACK_TYPE value)
27 {
28 assert(!is_full());
29 top_element += 1;
30 stack[top_element] = value;
31 }
32
33 STACK_TYPE pop(void)
34 {
35 STACK_TYPE temp;
36 assert(!is_empty());
37 temp = stack[top_element];
38 top_element -= 1;
39 return temp;
40 }
41
42 STACK_TYPE top(void)
43 {
44 assert(!is_empty());
45 return stack[top_element];
46 }
47 int is_empty(void)
48 {
49 assert(stack_size > 0);
50 return top_element == -1;
51 }
52 int is_full(void)
53 {
54 assert(stack_size > 0);
55 return top_element == stack_size - 1;
56 }
- 链表实现
C
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "stack.h"
4 #include <assert.h>
5
6 #define FALSE 0
7
8 typedef struct STACK_NODE{
9 STACK_TYPE value;
10 struct STACK_NODE *next;
11 }StackNode;
12
13 static StackNode *stack = NULL;
14
15 void create_stack(size_t size){
16 }
17
18 void destory_stack(void)
19 {
20 while(!is_empty())
21 pop();
22 }
23 void push(STACK_TYPE value)
24 {
25 StackNode *new_node;
26
27 new_node = malloc(sizeof(StackNode));
28 assert(new_node != NULL);
29 new_node->value = value;
30 new_node->next = stack;
31 stack = new_node;
32 }
33
34 STACK_TYPE pop(void)
35 {
36 StackNode *first_node;
37 STACK_TYPE temp;
38 if(!is_empty()){
39 first_node = stack;
40 temp = first_node->value;
41 stack = first_node->next;
42 free(first_node);
43
44 return temp;
45 }
46 return -1;
47 }
48
49 STACK_TYPE top(void)
50 {
51 assert(!is_empty());
52 return stack->value;
53 }
54 int is_empty(void){
55 return stack == NULL;
56 }
57 int is_full(void){
58 return FALSE;
59 }
队列FIFO
- 循环数组
当用一个指针指向头部,一个指向尾部的时候, 在为空的时候,需要把尾部放在比头部小一个的位置,这时候和数组满了的时候的格式一样
**解决方法:**重新定义数组满的概念, 数组中有一个元素一直不使用
满:
(rear + 2) % QUERE_SIZE == front
空:
(rear + 1) % QUERE_SIZE == front
C
1 #include "queue.h"
2 #include <stdio.h>
3 #include <assert.h>
4 #define QUEUE_SIZE 100
5 #define ARRAY_SIZE (QUEUE_SIZE + 1)
6
7 static QUEUE_TYPE queue[ARRAY_SIZE];
8 static size_t front = 1;
9 static size_t rear = 0;
10
11 void insert(QUEUE_TYPE value){
12 assert(!is_full());
13 rear = (rear+1) % ARRAY_SIZE;
14 queue[rear] = value;
15 }
16 void delect(void)
17 {
18 assert(!is_empty());
19 front = (front + 1) % ARRAY_SIZE;
20 }
21
22 QUEUE_TYPE first(void)
23 {
24 assert(!is_empty());
25 return queue[front];
26 }
27
28 int is_empty(void)
29 {
30 return (rear + 1) % ARRAY_SIZE == front;
31 }
32 int is_full(void)
33 {
34 return (rear + 2) % ARRAY_SIZE == front;
35 }
树
要么为空,要么具有零个或多个孩子, 每个孩子本身也是树, 递归的设置提示了树的高度没有内在的限制
二叉搜索树
树的特殊形式
- 每个节点最多有两个孩子, 左孩子, 右孩子
- 每个节点比他所有的左子树的值要大, 但是比右子树的值要小
- 排除了相等的可能性
没有孩子的节点被称为叶节点
在二叉树之中插入
树为空:
新的值作为根节点插入
否则:
小于当前:
插入左侧
否则:
右侧
在二叉树中删除
- 删除没有孩子的节点
- 删除有一个孩子
- 删除有两个: 删除左侧最的大一个值把他替换这个值
在二叉树中查找
树为空:
不存在
否则:
和根节点相等:
成功
否则:
小于:
左侧
否则:
右侧
树的遍历
有几种不同的次序
- 前序: 检查节点然后递归的遍历左子树右子树
- 中序: 先遍历左侧然后检测当前节点, 最后右侧
- 后序: 首先遍历左右然后当前节点
- 层次遍历: 处理根节点, 然后他的儿子, 然后孙子
实现
数组类型
- 节点N的双亲节点是N/2
- 节点N左孩子是2N
- 节点右孩子是2N+1
问题: 数组是从零开始的
- 忽略第一个元素
- 更改规则
- 节点N的双亲节点是(N+1)/2-1
- 节点N左孩子是2N+1
- 节点右孩子是2N+2
数组存在的问题:
- 不能够随意插入, 会导致浪费,
- 分配不同元素利用率不同
C
1 #include <stdio.h>
2 #include <assert.h>
3 #include "tree.h"
4
5 #define TREE_SIZE 100
6 #define ARRAY_SIZE (TREE_SIZE + 1)
7
8 static TREE_TYPE tree[ARRAY_SIZE];
9 //查询分支的位置
10 static int left_child(int current)
11 {
12 return current * 2;
13 }
14
15 static int right_child(int current)
16 {
17 return current * 2 + 1;
18 }
19 //插入一个元素
20 void insert(TREE_TYPE value)
21 {
22 int current;
23 assert(value != 0);
24 current = 1;
25 while(tree[current] != 0){//根据数值寻找位置所在
26 if(value < tree[current])
27 current = left_child(current);
28 else{
29 assert(value != tree[current]);//保证不会有重复的元素
30 current = right_child(current);
31 }
32 assert(current<ARRAY_SIZE);
33 }
34 tree[current] = value;
35 }
36
37 TREE_TYPE *find(TREE_TYPE value)
38 {
39 int current;
40 current = 1;
41 while(current < ARRAY_SIZE && tree[current] != value){
42 if(value < tree[current])
43 current = left_child(current);
44 else
45 current = right_child(current);
46 }
47 if(current < ARRAY_SIZE)
48 return tree + current;
49 else
50 return 0;
51 }
52 static void do_per_order_traverse(int current, void (*callback)(TREE_TYPE value))
53 {//遍历到每一个元素并进行一定的处理
54 if(current < ARRAY_SIZE && tree[current] != 0)
55 {
56 callback(tree[current]);
57 do_per_order_traverse(left_child(current), callback);
58 do_per_order_traverse(right_child(current), callback);
59 }
60 }
61 //外部可以使用的
62 void pre_order_traverse(void (*callback)(TREE_TYPE value))
63 {
64 do_per_order_traverse(1, callback);
65 }
链表类型
由于每一个节点必须有左右两个孩子, 所以用两个指针
C
1 #include "tree.h"
2 #include <assert.h>
3 #include <stdio.h>
4 #include <malloc.h>
5
6 typedef struct TREE_NODE{//用于存储数据的结构体
7 TREE_TYPE value;
8 struct TREE_NODE *left;
9 struct TREE_NODE *right;
10 }TreeNode;
11
12 static TreeNode *tree;//建立一个指向根部的指针
13
14 void insert(TREE_TYPE value)
15 {
16 TreeNode *current;
17 TreeNode **link;//用来指向上一级的指针的指针
18
19 link = &tree;//初始化为根部
20 while((current = *link) != NULL)
21 {
22 if(value < current->value)
23 link = ¤t->left;
24 else{
25 assert(value != current->value);
26 link = ¤t->right;
27 }
28 }
29
30 current = malloc(sizeof(TreeNode));
31 assert(current != NULL);
32 current->value = value;
33 current->left = NULL;
34 current->right = NULL;
35 *link = current;//创建与上一级的链接
36 }
37
38 TREE_TYPE *find(TREE_TYPE value)
39 {
40 TreeNode *current;
41 current = tree;//指向根部
42 while(current != NULL && current->value != value)
43 {
44 if(value<current->value)
45 current = current->left;
46 else
47 current = current->right;
48 }
49 if(current != NULL)
50 return ¤t->value;//返回一个指向数据的指针
51 else
52 return NULL;
53 }
54
55 static void do_pre_order_traverse(TreeNode *current,
56 void (*callback)(TREE_TYPE value))
57 {
58 if(current != NULL)
59 {
60 callback(current->value);//对每一个数据进行处理
61 do_pre_order_traverse(current->left, callback);//对左侧进行遍历
62 do_pre_order_traverse(current->right, callback);//右侧
63 }
64 }
65
66 void pre_order_traverse(void (*callback)(TREE_TYPE value))
67 {
68 do_pre_order_traverse(tree, callback);//外部调用
69 }
实现的改进
拥有超过一个堆栈
由用户进行对堆栈的创建,再通过参数进行访问, 用户可以创建任意数量的堆栈, 然后进行访问
拥有超过一种的类型
- 为每一种类型写代码
- 实现#define的宏
- 使用void *类型
名字冲突
不同的结构之间可能有相同的函数名字
标准库的ADT
C语言暂时没有提供,但是可以用#define模拟
g_atck.h
C
1 #include <assert.h>
2 /*建立一个栈初始化的声明,数据类型,标记,大小*/
3 #define GENERIC_STACK(STACK_TYPE, SUFFIX, STACK_SIZE) \
4 static STACK_TYPE stack##SUFFIX[STACK_SIZE]; \
5 static int top_element##SUFFIX = -1; \
6 int is_empty##SUFFIX(void) \
7 { \
8 return top_element##SUFFIX == -1; \
9 } \
10 int is_full##SUFFIX(void) \
11 { \
12 return top_element##SUFFIX == STACK_SIZE -1; \
13 } \
14 void push##SUFFIX(STACK_TYPE value) \
15 { \
16 assert(!is_full##SUFFIX()); \
17 top_element##SUFFIX += 1; \
18 stack##SUFFIX[top_element##SUFFIX] = value; \
19 } \
20 void pop##SUFFIX(void) \
21 { \
22 assert(!is_empty##SUFFIX()); \
23 top_element##SUFFIX -= 1; \
24 } \
25 STACK_TYPE top##SUFFIX(void) \
26 { \
27 assert(!is_empty##SUFFIX()); \
28 return stack##SUFFIX[top_element##SUFFIX]; \
29 }
g_atck.c
C
2 #include <stdio.h>
3 #include "g_atck.h"
4
5 GENERIC_STACK(int, _int, 10)//建立第一个栈
6 GENERIC_STACK(float, _float, 10)
7 int main(){
8 push_int(5);
9 push_int(22);
10 push_int(15);
11 push_float(25.4);
12 push_float(-40.5);
13
14 while(!is_empty_int()){
15 printf("*Popping %d\n", top_int());
16 pop_int();
17 }
18 while(!is_empty_float()){
19 printf("*Popping %f\n", top_float());
20 pop_float();
21 }
22 return EXIT_SUCCESS;
23 }
result:
*Popping 15
*Popping 22
*Popping 5
*Popping -40.500000
*Popping 25.400000