常用数据结构.docx
《常用数据结构.docx》由会员分享,可在线阅读,更多相关《常用数据结构.docx(42页珍藏版)》请在冰豆网上搜索。
常用数据结构
一线性队列
这里的线性结构实际上指的就是连续内存的意思,只不过使用“线性”这个词显得比较专业而已。
前面一篇博客介绍了现象结构的处理方法,那么在这个基础之上我们是不是添加一些属性形成一种新的数据结构类型呢?
答案是肯定的,队列便是其中的一种。
队列的性质很简单:
(1)队列有头部和尾部
(2)队列从尾部压入数据
(3)队列从头部弹出数据
那么连续内存下的队列是怎么实现的呢?
a)设计队列数据结构
[cpp] viewplaincopy
1.typedef struct _QUEUE_NODE
2.{
3. int* pData;
4. int length;
5. int head ;
6. int tail;
7. int count;
8.}QUEUE_NODE;
b)申请队列内存
[cpp] viewplaincopy
1.QUEUE_NODE* alloca_queue(int number)
2.{
3. QUEUE_NODE* pQueueNode;
4. if( 0 == number)
5. return NULL;
6.
7. pQueueNode = (QUEUE_NODE*)malloc(sizeof(QUEUE_NODE));
8. assert(NULL !
= pQueueNode);
9. memset(pQueueNode, 0, sizeof(QUEUE_NODE));
10.
11. pQueueNode->pData = (int*)malloc(sizeof(int) * number);
12. if(NULL == pQueueNode->pData){
13. free(pQueueNode);
14. return NULL;
15. }
16.
17. pQueueNode->length = number;
18. return pQueueNode;
19.}
c)释放队列内存
[cpp] viewplaincopy
1.STATUS delete_queue(const QUEUE_NODE* pQueueNode)
2.{
3. if(NULL == pQueueNode)
4. return FALSE;
5.
6. assert(NULL !
= pQueueNode->pData);
7.
8. free(pQueueNode->pData);
9. free((void*)pQueueNode);
10. return TRUE;
11.}
d)把数据压入队列
[cpp] viewplaincopy
1.STATUS insert_queue(QUEUE_NODE* pQueueNode, int value)
2.{
3. if(NULL == pQueueNode)
4. return FALSE;
5.
6. if(pQueueNode->length == pQueueNode->count)
7. return FALSE;
8.
9. pQueueNode->tail = (pQueueNode->tail + 1) % pQueueNode->length;
10. pQueueNode->pData[pQueueNode->tail] = value;
11. pQueueNode->count ++;
12. return TRUE;
13.}
e)把数据弹出队列
[cpp] viewplaincopy
1.STATUS get_queue_data(QUEUE_NODE* pQueueNode, int* value)
2.{
3. if(NULL == pQueueNode || NULL == value)
4. return FALSE;
5.
6. if(0 == pQueueNode->count)
7. return FALSE;
8.
9. *value = pQueueNode->pData[pQueueNode->head];
10. pQueueNode-> pData[pQueueNode->head] = 0;
11. pQueueNode-> count --;
12. pQueueNode->head = (pQueueNode->head + 1) % pQueueNode->length;
13. return TRUE;
14.}
f)统计当前队列中有多少数据
[cpp] viewplaincopy
1.int get_total_number(const QUEUE_NODE* pQueueNode)
2.{
3. if(NULL == pQueueNode)
4. return 0;
5.
6. return pQueueNode->count;
7.}
g)查看队列中初始化的时候总长度是多少
[cpp] viewplaincopy
1.int get_total_number(const QUEUE_NODE* pQueueNode)
2.{
3. if(NULL == pQueueNode)
4. return 0;
5.
6. return pQueueNode->length;
7.}
二线性堆栈
前面我们讲到了队列,今天我们接着讨论另外一种数据结构:
堆栈。
堆栈几乎是程序设计的命脉,没有堆栈就没有函数调用,当然也就没有软件设计。
那么堆栈有什么特殊的属性呢?
其实,堆栈的属性主要表现在下面两个方面:
(1)堆栈的数据是先入后出
(2)堆栈的长度取决于栈顶的高度
那么,作为连续内存类型的堆栈应该怎么设计呢?
大家可以自己先试一下:
(1)设计堆栈节点
[cpp] viewplaincopy
1.typedef struct _STACK_NODE
2.{
3. int* pData;
4. int length;
5. int top;
6.}STACK_NODE;
(2)创建堆栈
[cpp] viewplaincopy
1.STACK_NODE* alloca_stack(int number)
2.{
3. STACK_NODE* pStackNode = NULL;
4. if(0 == number)
5. return NULL;
6.
7. pStackNode = (STACK_NODE*)malloc(sizeof(STACK_NODE));
8. assert(NULL !
= pStackNode);
9. memset(pStackNode, 0, sizeof(STACK_NODE));
10.
11. pStackNode->pData = (int*)malloc(sizeof(int) * number);
12. if(NULL == pStackNode->pData){
13. free(pStackNode);
14. return NULL;
15. }
16.
17. memset(pStackNode->pData, 0, sizeof(int) * number);
18. pStackNode-> length = number;
19. pStackNode-> top= 0;
20. return pStackNode;
21.}
(3)释放堆栈
[cpp] viewplaincopy
1.STATUS free_stack(const STACK_NODE* pStackNode)
2.{
3. if(NULL == pStackNode)
4. return FALSE;
5.
6. assert(NULL !
= pStackNode->pData);
7.
8. free(pStackNode->pData);
9. free((void*)pStackNode);
10. return TRUE;
11.}
(4)堆栈压入数据
[cpp] viewplaincopy
1.STATUS stack_push(STACK_NODE* pStackNode, int value)
2.{
3. if(NULL == pStackNode)
4. return FALSE;
5.
6. if(pStackNode->length = pStackNode->top)
7. return FALSE;
8.
9. pStackNode->pData[pStackNode->top ++] = value;
10. return TRUE;
11.}
(5)堆栈弹出数据
[cpp] viewplaincopy
1.STATUS stack_pop(STACK_NODE* pStackNode, int* value)
2.{
3. if(NULL == pStackNode || NULL == value)
4. return FALSE;
5.
6. if(0 == pStackNode->top)
7. return FALSE;
8.
9. *value = pStackNode->pData[-- pStackNode->top];
10. return TRUE;
11.}
(6)统计当前堆栈中包含多少数据
[cpp] viewplaincopy
1.int count_stack_number(const STACK_NODE* pStackNode)
2.{
3. return pStackNode->top;
4.}
建议:
堆栈是函数调用的基础,是递归调用的基础,是很多问题的根源,建议朋友们平时有时间好好练习一下。
三单向链表
有的时候,处于内存中的数据并不是连续的。
那么这时候,我们就需要在数据结构中添加一个属性,这个属性会记录下面一个数据的地址。
有了这个地址之后,所有的数据就像一条链子一样串起来了,那么这个地址属性就起到了穿线连结的作用。
相比较普通的线性结构,链表结构的优势是什么呢?
我们可以总结一下:
(1)单个节点创建非常方便,普通的线性内存通常在创建的时候就需要设定数据的大小
(2)节点的删除非常方便,不需要像线性结构那样移动剩下的数据
(3)节点的访问方便,可以通过循环或者递归的方法访问到任意数据,但是平均的访问效率低于线性表
那么在实际应用中,链表是怎么设计的呢?
我们可以以int数据类型作为基础,设计一个简单的int链表:
(1)设计链表的数据结构
[cpp] viewplaincopy
1.typedef struct _LINK_NODE
2.{
3. int data;
4. struct _LINK_NODE* next;
5.}LINK_NODE;
(2)创建链表
[cpp] viewplaincopy
1.LINK_NODE* alloca_node(int value)
2.{
3. LINK_NODE* pLinkNode = NULL;
4. pLinkNode = (LINK_NODE*)malloc(sizeof(LINK_NODE));
5.
6. pLinkNode->data = value;
7. pLinkNode->next = NULL;
8. return pLinkNode;
9.}
(3)删除链表
[cpp] viewplaincopy
1.void delete_node(LINK_NODE** pNode)
2.{
3. LINK_NODE** pNext;
4. if(NULL == pNode || NULL == *pNode)
5. return ;
6.
7. pNext = &(*pNode)->next;
8. free(*pNode);
9. delete_node(pNext);
10.}
(4)链表插入数据
[cpp] viewplaincopy
1.STATUS _add_data(LINK_NODE** pNode, LINK_NODE* pDataNode)
2.{
3. if(NULL == *pNode){
4. *pNode = pDataNode;
5. return TRUE;
6. }
7.
8. return _add_data(&(*pNode)->next, pDataNode);
9.}
10.
11.STATUS add_data(const LINK_NODE** pNode, int value)
12.{
13. LINK_NODE* pDataNode;
14. if(NULL == *pNode)
15. return FALSE;
16.
17. pDataNode = alloca_node(value);
18. assert(NULL !
= pDataNode);
19. return _add_data((LINK_NODE**)pNode, pDataNode);
20.}
(5)删除数据
[cpp] viewplaincopy
1.STATUS _delete_data(LINK_NODE** pNode, int value)
2.{
3. LINK_NODE* pLinkNode;
4. if(NULL == (*pNode)->next)
5. return FALSE;
6.
7. pLinkNode = (*pNode)->next;
8. if(value == pLinkNode->data){
9. (*pNode)->next = pLinkNode->next;
10. free(pLinkNode);
11. return TRUE;
12. }else{
13. return _delete_data(&(*pNode)->next, value);
14. }
15.}
16.
17.STATUS delete_data(LINK_NODE** pNode, int value)
18.{
19. LINK_NODE* pLinkNode;
20. if(NULL == pNode || NULL == *pNode)
21. return FALSE;
22.
23. if(value == (*pNode)->data){
24. pLinkNode = *pNode;
25. *pNode = pLinkNode->next;
26. free(pLinkNode);
27. return TRUE;
28. }
29.
30. return _delete_data(pNode, value);
31.}
(6)查找数据
[cpp] viewplaincopy
1.LINK_NODE* find_data(const LINK_NODE* pLinkNode, int value)
2.{
3. if(NULL == pLinkNode)
4. return NULL;
5.
6. if(value == pLinkNode->data)
7. return (LINK_NODE*)pLinkNode;
8.
9. return find_data(pLinkNode->next, value);
10.}
(7)打印数据
[cpp] viewplaincopy
1.void print_node(const LINK_NODE* pLinkNode)
2.{
3. if(pLinkNode){
4. printf("%d\n", pLinkNode->data);
5. print_node(pLinkNode->next);
6. }
7.}
(8)统计数据
[cpp] viewplaincopy
1.int count_node(const LINK_NODE* pLinkNode)
2.{
3. if(NULL == pLinkNode)
4. return 0;
5.
6. return 1 + count_node(pLinkNode->next);
7.}
四双向链表
前面的博客我们介绍了单向链表。
那么我们今天介绍的双向链表,顾名思义,就是数据本身具备了左边和右边的双向指针。
双向链表相比较单向链表,主要有下面几个特点:
(1)在数据结构中具有双向指针
(2)插入数据的时候需要考虑前后的方向的操作
(3)同样,删除数据的是有也需要考虑前后方向的操作
那么,一个非循环的双向链表操作应该是怎么样的呢?
我们可以自己尝试一下:
(1)定义双向链表的基本结构
[cpp] viewplaincopy
1.typedef struct _DOUBLE_LINK_NODE
2.{
3. int data;
4. struct _DOUBLE_LINK_NODE* prev;
5. struct _DOUBLE_LINK_NODE* next;
6.}DOUBLE_LINK_NODE;
(2)创建双向链表节点
[cpp] viewplaincopy
1.DOUBLE_LINK_NODE* create_double_link_node(int value)
2.{
3. DOUBLE_LINK_NODE* pDLinkNode = NULL;
4. pDLinkNode = (DOUBLE_LINK_NODE*)malloc(sizeof(DOUBLE_LINK_NODE));
5. assert(NULL !
= pDLinkNode);
6.
7. memset(pDLinkNode, 0, sizeof(DOUBLE_LINK_NODE));
8. pDLinkNode->data = value;
9. return pDLinkNode;
10.}
(3)删除双向链表
[cpp] viewplaincopy
1.void delete_all_double_link_node(DOUBLE_LINK_NODE** pDLinkNode)
2.{
3. DOUBLE_LINK_NODE* pNode;
4. if(NULL == *pDLinkNode)
5. return ;
6.
7. pNode = *pDLinkNode;
8. *pDLinkNode = pNode->next;
9. free(pNode);
10. delete_all_double_link_node(pDLinkNode);
11.}
(4)在双向链表中查找数据
[cpp] viewplaincopy
1.DOUBLE_LINK_NODE* find_data_in_double_link(const DOUBLE_LINK_NODE* pDLinkNode, int data)
2.{
3. DOUBLE_LINK_NODE* pNode = NULL;
4. if(NULL == pDLinkNode)
5. return NULL;
6.
7. pNode = (DOUBLE_LINK_NODE*)pDLinkNode;
8. while(NULL !
= pNode){
9. if(data == pNode->data)
10. return pNode;
11. pNode = pNode ->next;
12. }
13.
14. return NULL;
15.}