資料結構與演算法
資料結構研究程式裡如何使用儲存區存放數字,演算法研究解決一些常見問題的通用方法。數字之間的關係可以從兩個完全不同的角度描述。
邏輯關係(邏輯結構)描述數字之間與計算機無關的關係;物理關係(物理結構)描述存放數字的儲存區之間的關係。
邏輯結構
1.集合結構:所有的數字可以被看做一個整體
2.線性結構:如果可以用一條有順序的線把所有數字串起來則數字之間是線性結構的
3.樹狀結構:所有資料都是從一個資料向同一個方向擴充套件出來的,任何數字可以擴充出多個其他數字
4.網狀結構:任何兩個數字之間都可以有直接的聯絡,所有數字之間的聯絡沒有統一的方向
物理結構
1.順序結構:所有儲存區在記憶體裡連續排列,陣列和動態分配記憶體都是順序結構的例子,順序結構裡每個儲存區都有一個編號,可以直接透過編號找到對應的儲存區。順序結構可以不透過其他儲存區直接找到需要的儲存區,這種能力叫隨機訪問能力。順序結構裡的儲存區個數很難調整,容易造成記憶體浪費。
2.鏈式結構:由多個相互獨立的儲存區構成,任何兩個儲存區之間可以用指標連線,鏈式物理結構裡每個儲存區都是一個結構體型別的儲存區,他們叫做節點,單向線性鏈式物理結構中任何兩個節點之間都有前後順序(每個節點裡只需要包含一個指標)。單向線性鏈式物理結構裡最後一個節點裡的指標必須是空指標。可以在單向線性鏈式物理機構中最前面的節點前再增加一個節點,這個節點叫頭節點。可以在單向線性鏈式物理結構中最後面的節點後再增加一個節點,這個節點叫尾節點。
鏈式物理結構不支援隨機訪問能力,鏈式物理結構適合進行插入或刪除操作,鏈式物理結構裡的節點個數可以隨時調整,
/*
*
*鏈式物理結構固定程式碼
*
* */
#include<stdio.h>
typedef struct node {
int num;
struct node *p_next;
} node;
int main (){
node node1 = {10}, node2 = {20}, node3 = {30},head = {0}, tail = {0};
node *p_node = NULL;
head.p_next = &node1;
node1.p_next = &node2;
node2.p_next = &node3;
node3.p_next = &tail;
/*
*p_node指標迴圈變數會從頭節點開始
*向後依次捆綁每個節點,直到最後一個有效節點為止
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
/*
*以下三個指標和三個相鄰節點捆綁
* 其中p_first指標捆綁的節點在最前面
*p_last指標捆綁的節點在最後
*p_mid指標捆綁的節點在中間
*p_mid指標在迴圈過程中從第一個有效節點一直捆綁到尾節點
* */
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
return 0;
}
插入刪除操作
/*
*
*鏈式結構
*
* */
#include<stdio.h>
typedef struct node {
int num;
struct node *p_next;
} node;
int main (){
node node1 = {10}, node2 = {20}, node3 = {30},head = {0}, tail = {0};
node node4 = {15};
node *p_node = NULL;
head.p_next = &node1;
node1.p_next = &node2;
node2.p_next = &node3;
node3.p_next = &tail;
/*
*p_node指標迴圈變數會從頭節點開始
*向後依次捆綁每個節點,直到最後一個有效節點為止
*
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
/*
*以下三個指標和三個相鄰節點捆綁
* 其中p_first指標捆綁的節點在最前面
*p_last指標捆綁的節點在最後
*p_mid指標捆綁的節點在中間
*p_mid指標在迴圈過程中從第一個有效節點一直捆綁到尾節點
*
* */
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
/*
*在鏈式結構裡
*刪除資料
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail && p_mid->num == 20){
p_first->p_next = p_last;
break;
}
}
/*
*鏈式結構插入資料
*並保證排序從小到大
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid == &tail || p_mid->num > node4.num){
p_first->p_next = &node4;
node4.p_next = p_mid;
break;
}
}
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
return 0;
}
資料結構
資料結構由一組儲存區和相關的管理函式構成,這些函式提供了對儲存區的使用方法,程式裡的其他語句都只能透過這些函式使用這些儲存區。
棧
棧是一種資料結構,棧可以用來存放數字,棧裡的數字有前後順序,先進入棧的數字在前,後進入棧的數字在後,每次只能從棧裡獲得一個數字,這個數字必須是最後一個放進去的數字,這種使用數字的方法叫後進先出。
push和pop
編寫棧的時候需要提供一個函式用來向棧里加入數字,這個函式名稱通常叫push,編寫棧的時候需要提供一個函式用來從棧裡獲得數字,這個函式名稱通常叫pop。
棧練習:編寫程式從鍵盤得到多個非負整數,把他們倒序顯示在螢幕上,要求用棧實現。
/*
*棧演示主函式
* */
#include<stdio.h>
#include"1stack.h"
int main (){
int num = 0;
stack stk = {0};
stack_init(&stk);
while (1){
printf("請輸入一個數字:");
scanf("%d",&num);
if (num < 0){
break;
}
if (!stack_push(&stk,num)){
break;
}
}
while (1){
if (!stack_pop(&stk,&num)){
break;
}
printf("%d ",num);
}
printf("\n");
stack_deinit(&stk);
return 0;
}
/*
*棧演示標頭檔案
* */
#ifndef _1STACK_H_
#define _1STACK_H_
typedef struct {
int buf[SIZE];
int qty;
} stack;
//棧初始化函式
void stack_init (stack *);
//棧清理函式
void stack_deinit (stack *);
//統計棧裡有效數字個數
int stack_size (const stack *);
//判斷棧是否已經滿了
int stack_full (const stack *);
//判斷棧是否為空
int stack_empty (const stack *);
//向棧里加入數字的函式
int stack_push (stack *,int);
//從棧裡獲得數字的函式(從棧裡刪除數字)
int stack_pop (stack *,int *);
//從棧裡獲得數字的函式(不會刪除數字)
int stack_top (const stack *,int *);
#endif //_1STACK_H_
/*
*棧演示原始檔
* */
#include"1stack.h"
//棧初始化函式
void stack_init (stack *p_stack){
p_stack->qty = 0;
}
//棧清理函式
void stack_deinit (stack *p_stack){
p_stack->qty = 0;
}
//統計棧裡有效數字個數
int stack_size (const stack *p_stack){
return p_stack->qty;
}
//判斷棧是否已經滿了
int stack_full (const stack *p_stack){
return p_stack->qty >= SIZE;
}
//判斷棧是否為空
int stack_empty (const stack *p_stack){
return !(p_stack->qty);
}
//向棧里加入數字的函式
int stack_push (stack *p_stack,int num){
if (p_stack->qty >= SIZE){
return 0;
}
p_stack->buf[p_stack->qty] = num;
p_stack->qty++;
return 1;
}
//從棧裡獲得數字的函式(從棧裡刪除數字)
int stack_pop (stack *p_stack,int *p_num){
if (!(p_stack->qty)){
return 0;
}
*p_num = p_stack->buf[p_stack->qty - 1];
p_stack->qty--;
return 1;
}
//從棧裡獲得數字的函式(不會刪除數字)
int stack_top (const stack *p_stack,int *p_num){
if (!(p_stack->qty)){
return 0;
}
*p_num = p_stack->buf[p_stack->qty - 1];
return 1;
}
說明:程式執行後,根據資料輸入非負數,資料輸入完成時,以負數結束輸入。