C指針(4)——數據結構中指針的應用(非常重要)


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);
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM