5-1動態內存分配,分配的是堆內存的空間
- 分配內存函數 (都集中在庫函數 stdlib.h 中)
void *malloc (unsigned int num_bytes); //指定分配內存空間大小,大小為 num_bytes字節,其值是隨機值。 void *calloc (unsigned num ,unsigned size); //參數包含元素的數量和每個元素的字節數,內存空間為num*sie void *realloc(void *ptr,size_t size); //調用該函數對內存空間進行重新分配,ptr指向已有的內存空間,size用來指定重新分配后所得整個空間大小
在使用動態分配之前,首先要判斷是否分配成功。
- 內存的釋放函數原型:
void free(void *ptr); //動態分配的內存使用結束后,要及時釋放,
內存釋放后建議把指針指向NULL
5-2隊列(初始化,入隊,出隊,判斷空,判斷滿)
-
單向隊列

- 循環隊列 (隊頭和隊尾有兩種情況會指向同一位置,一是隊列空了,二是隊列滿了)
#define QueueSize_UartLen 8
typedef struct
{
int front; //隊頭
int rear; //隊尾
int counter; //記錄隊列元素的個數
int uart_data[QueneSize_UartLen]; //定義數組用來存儲隊列的元素
}CIRQUEUE_UART; //定義結構體,用typedef把結構體重新命名為CIRQUEUE_UART
void InitQueue(CIRQUEUE_UART *queue) //初始化形參,CIRQUEUE_UART類型的指針變量queue,隊列的初始化
{
queue->front=0; //->與指向結構體變量的指針相連,表示指向結構體變量指針的成員(左邊為指針,注意與 . 的區別)
queue->rear=0;
queue->counter=0;
}
int Inqueue(CIRQUEUE_UART *queue,int data) //入隊
{
if(QueueFull(queue)) //隊滿判斷
{
return 0; //輸出隊滿提示
}
else
{
queue->uart_data[queue->rear]=data; //queue->rear指向隊尾待插入數據位置
queue->counter++; //計數器加1
queue->rear=(queue->rear+1)%QueueSize_UartLen; //然后指queue->rear向下一個待插入數據的位置
return 1;
}
}
int OutQueue(CIRQUEUE_UART *queue,int *p_data) //出隊 通過指針p_data取出隊的數據
{
if(QueueEmpty(queue))
{
return 0;
}
else
{
*p_data=queue->data(front); //先把出隊的數據元素取出放在p_data
queue->counter--; //計數器減1
queue->front=(queue->front+1)%QueueSize_UartLen; //然后指queue->front向下一個位置
return 1;
}
}
int QueueEmpty(CIRQUEUE_UART *queue) //判斷隊空
{
return queue->count==0;
}
int QueueFull(CIRQUEUE_UART *queue) //判斷隊滿
{
return queue->counter==QueueSize_UartLen;
}
-
鏈式隊列
構成鏈表中的每一個存儲節點都包括一個值域和一個指針域,而指針域指向的是下一個存儲節點的,則稱該鏈表為單鏈表,在一個單鏈表中第一個節點的指針稱為表頭指針,最后一個節點被稱為表尾節點,表尾節點的指針域的值為空。
在一個單鏈表中,除表頭節點外,每一個每個結點都由前一個結點的指針域所指向,第一個節點只能有另設的表頭指針所指向。當訪問一個單鏈表時候,只能從表頭指針出發,首先訪問第一個節點,然后由第一個節點的指針域指向第二個節點的地址。
空隊列時,頭指針front和尾指針rear都指向頭結點。

單鏈表只要頭節點就可以訪問所有的鏈表元素,融入隊列的特性,一個指針是不不夠的。隊列刪除(出隊)在對頭,插入(入隊)在隊尾。
頭指針front指向鏈隊的頭結點,每個結點對應一個數據;尾指針rear指向終端結點。
typedef struct LinkNode_t //隊列結點結構
{
int data;
struct LinkNode_t *next;
}LinkNode;
typedef struct LinkPoint_t //隊列鏈表結構
{
struct LinkNode_t *front;
struct LinkNode_t *rear;
}LinkQueue;
LinkQueue *queue;
LinkNode *node;
LinkQueue LinkQueueInit() //初始化,建立一個帶有隊頭的空隊列
{
queue_t=(LinkQueue)malloc(sizeof(LinkQueue)); //因為在LinkQueue中定義了一個結構體指針,必須對其進行分配內存。
node=(LinkQueue)malloc(sizeof(LinkNode)); //同上
node->next=NULL;
queue_t->front=queue->rear=node;
return queue_t;
}
void InlinkQueue(LinkQueue *queue,int data) //入隊
{
node=(LinkNode)malloc(sizeof(LinkNode));
node->data=data;
node->next=queue->rear->next; //node->next指向data插入前、尾部指針指向的next的位置(queue->rear->next)。
queue->rear->next=node; //新插入的數據或結點成了新的隊尾,隊尾指針queue->rear->next指向新插入的node結點
queue->rear=node; //最后讓新插入的數據或結點成為新的隊尾。
}
void OutQueue(LinkQueue *queue) //出隊
{
int data;
if(!LQEmpty(queue)) //判空
{
node=queue->front->next;
queue->front->next=node->next;
data=node->data;
if(node==queue->rear) //如果只有一個元素,當他出隊時隊列就成了空隊,需要修改為尾部指針
{
queue->rear=queue->front;
}
free(node);
return data;
}
}
int LQEmpty(LinkQueue *queue) //對空判斷
{
if(queue->front==queue->rear)
{
return 1;
}
else
{
return 0;
}
}
鏈隊沒有判斷隊滿的情況,實際操作沒有必要,因為鏈隊繼承了動態鏈表的操作。入隊時會進行動態分配內存空間,出隊時,則進行了釋放。
補充:結構體中定義另外一個結構體。
5-3堆棧(初始化,進棧,出棧,棧空的判斷,棧滿的判斷,取棧頂元素)

1.采用指向棧頂、棧底兩個指針時:
typedef struct
{
int top;
int data[10];
int bottom;
}stack_rf;
stack_rf *p_rf_data; //
p_rf_data->top-p_rf_data->bottom==10; //判斷棧是否為滿
p_rf_data->top-p_rf_data->bottom==0; //判斷棧是否為空
*(p_rf_data->top-1) //棧頂元素
*(p_rf_data->bottom) //棧底元素
2.只采用棧頂一個指針時:
#define Len_Uart 10;
typedef struct
{
int top;
int uart_data[Len_Uart];
}stack_uart;
void InitStack(stack_uart *stack)
{
stack->top=0; //棧的初始化
}
int PushStack(stack_uart *stack,int data) //進棧
{
if(StackFull(stack)) //棧滿判斷
{
//輸出棧滿提示
return(0);
}
else
{
stack->uart_data[stack->top]=data;
stack->top++;
return(1);
}
}
int PopStack(stack_uart *stack) //出棧
{
int data;
if(StackEmpty(stack)) //棧空判斷
{
return 0; //輸出棧空提示
}
else
{
stack->top--;
data=stack->data[stack->top];
return data;
}
}
int StackEmpty(stack_uart *stack) //棧空判斷
{
return(stack->top==0);
}
int StackFull(stack_uart *stack) //棧滿判斷
{
return(stack->top==Len_Uart);
}
int StackTop(stack_uart *stack,int *p_topdata) //取棧頂元素
{
if(StackEmpty())
{
//輸出棧空提示
return 0;
}
else
{
*p_topdata=stack->uart_data[stack->top-1];
return 1;
}
}
5-4鏈表(鏈表建立,鏈表初始化,鏈表插入,鏈表刪除)

單鏈表:頭結點沒有值域,只有鏈域。專門存放第一個結點的地址。尾結點,有值域也有鏈域,鏈域始終為NULL。

循環鏈表:由單鏈表演變而來,循環鏈表的尾結點的鏈域指向鏈表頭的結點
兩者區別:(1)單鏈表需要專門創建一個頭結點存放第一個節點的地址,尾部節點鏈域為NULL;循環鏈表不需要,只是最后一個鏈域指向表頭結點。
(2)鏈尾判斷,單鏈表只需要判斷鏈域值是否為NULL;循環鏈表只需要判斷該結點的鏈域值是否指向鏈表頭結點。
typedef struct LinkNode //定義鏈表結點類型
{
int data;
struct LinkNode *next;
}*List;
List InitList(void) //鏈表初始化
{
List linknode_t;
linknode_t=(list)malloc(sozeof(list)); //動態分配內存空間給linknode_t
if(!linknode_t)
{
//輸出失敗提示
return 0;
}
else
{
linknode_t->next=linknode_t;
return linknode_t;
}
}
List g_DebiList;
int main(void) //在main中調用InitList()函數
{
g_DevList=InitList();
return 0;
}
鏈表的操作
typedef struct DEVICE_PROPERTY_t
{
int data;
struct DEVICE_PROPERTY_t *next;
}DEVICE_PROPERTY_t;
typedef unsigned char uint8_t;
#define DEVICELIST_MAX 5;
DEVICE_PROPERTY_t g_RegDeviceList[DEVICELIST_MAX];
DEVICE_PROPERTY_t *g_LastRegDevice;
uint8_t g_DeviceCnt=0;
voide DevListInit(void) //鏈表初始化
{
uint8_t i;
for(i=0;i<DEVICELIST_MAX;i++)
{
g_RegDeviceList[i].next=NULL;
}
g_lastRegDevice=NULL;
g_DeviceCnt=0
}
DEVICE_PROPERTY_t *FindVacancy(void) //尋找插入點
{
uint8_t i;
if(g_DeviceCnt<DEVICELIST_MAX)
{
for(i=0;i<EEVICELIST_MAX;i++)
{
if(g_RegDeviceList[i].next==NULL)
{
return(&g_RegDeviceList[i]);
}
}
return 0;
}
else
{
return(NULL);
}
}
uint8_t Insert(uint8_t data) //鏈表插入
{
DEVICE_PROPERTY_t *tempptr,*ptr;
temptr =FindVacancy();
if(tempptr!=null)
{
tempptr->data=data;
if(!g_DeviceCnt)
{
tempptr->next=tmpptr;
g_ListRegDevice=tempptr;
}
else
{
ptr=g_LastRegDevice->next; //暫存原來尾結點的鏈域
g_LastRegDevice->next=tempptr; //讓原來尾結點的鏈域指向新插入的結點,即g_LastRegDevice->next=tempptr;
tempptr->next=ptr; //讓新插入的結點成為尾結點,即鏈域指向原來尾結點鏈域所指向的位置
}
g_DeviceCnt++;
return 1;
}
return 0;
}
uint8_t DeleteDevicefromlist(uint8_t data) //鏈表刪除(data即為要刪除的結點)
{
uint8_t i,j;
DEVICE_PROPERTY_t *nextdevice;
for(i=0;i<g_DeviceCnt;i++) //通過判斷來指定結點的值域是否是要刪除的值域data
{
if(g_LastRegDevice->data==data)
{
nextdevce=g_ListRegDevice->next; //將該結點的鏈域指向的結點指針,也就是將要刪除的結點的下一個結點
g_LastRegDevice->next=NULL; //把該節點的鏈域置為NULL
g_DeviceCnt--;
g_LastRegDevice=nextdevice; //讓刪除結點的下一個結點成為尾結點(刪除是操作的逆過程)
if(g_DeviceCnt)
{
for(j=0;j<g_DeviceCnt-1;j++)
{
g_ListRegDevice->next=nextdevice;
}
g_ListRegDevice->next=nextdevice;
}
return 1;
}
g_LastRegDevice=g_LastRegDevice->next;
}
return 0;
}
5-5樹
樹為非線性結構,對於二叉樹首先是二叉樹的創建和遍歷
typedef struct tree_node //運用鏈式存儲結構
{
char data; //一個值域兩個鏈域,值域存儲結點本身的數值
struct tree_node *lchild,*rchild; //鏈域分別存儲結點左子樹和又子樹的地址
}BT_Node;
BT_Node *tree; //定義一個指針tree,用來執行那個即將建立的樹
BT_Node *Creat_BTree(BT_Node *tree) //創建二叉樹
{
char ch;
ch=getchar(); //從鍵盤輸入一個字符,
if(ch=='*')
{
tree=NULL;
}
else //如果不是字符,則分配存儲空間
{
tree=(BT_Node *)malloc(Tree_NodeLen);
tree->data=ch;
tree->lchild=Creat_BTree(tree->lchild); //調用創建函數本身,創建結點的左子樹和又子樹(這是函數的遞歸調用)
tree->rchild=Creat_BTree(tree->rchild);
}
return(tree);
}
二叉樹分為:前序遍歷,中序遍歷,后序遍歷
示例:二叉樹3種遍歷方式的應用
#include <stdio.h>
#include <malloc.h>
typedef struct tree_node
{
char data;
struct tree_node *lchild,*rchild;
}BT_Node;
#define Tree_NodeLen sizeof(BT_Node)
BT_Node *tree;
BT_Node *Creat_BTree(BT_Node *tree);
void Visit_Node(BT_Node *tree);
void Pre_Order(BT_Node *tree);
void Mid_Order(BT_Node *tree);
void After_Order(BT_Node *tree);
int main(void)
{
printf("請輸入樹節點:\n");
tree=Creat_BTree(tree);
if(tree)
{
printf("\n前序遍歷:\n");
Pre_Order(tree);
printf("\n");
printf("\n中序遍歷:\n");
Mid_Order(tree);
printf("\n");
printf("\n后序遍歷:\n");
After_Order(tree);
printf("\n");
}
printf("\n");
return 0;
}
BT_Node *Creat_BTree(BT_Node *tree)
{
char ch;
ch=getchar();
if(ch=='*')
{
tree=NULL;
}
else
{
tree=(BT_Node *)malloc(Tree_NodeLen);
tree->data=ch;
tree->lchild=Creat_BTree(tree->lchild);
tree->rchild =Creat_BTree(tree->rchild );
}
return(tree);
}
void Visit_Node(BT_Node *tree)
{
printf(" ");
putchar(tree->data);
printf("\t");
}
void Pre_Order(BT_Node *tree)
{
if(!tree)
{
return ;
}
else
{
Visit_Node(tree);
Pre_Order(tree->lchild );
Pre_Order(tree->rchild );
}
}
void Mid_Order(BT_Node *tree)
{
if(!tree)
{
return;
}
else
{
Mid_Order(tree->lchild );
Visit_Node(tree);
Mid_Order(tree->rchild );
}
}
void After_Order(BT_Node *tree)
{
if(!tree)
{
return;
}
else
{
After_Order(tree->lchild );
After_Order(tree->rchild );
Visit_Node(tree);
}
}


