《啊哈!演算法》-第 2 章:棧、佇列、連結串列

_我已經從中二畢業了發表於2019-05-13

佇列

概念

佇列是一種特殊的線性結構,它只允許在佇列的首部(head)進行刪除,這稱為“出隊”,而在佇列的尾部(tail)進行插入操作,這稱為“入隊”。當佇列中沒有元素時(即head == tail),稱為空佇列。

佇列具有“先進先出”(Frist In Frist Out, FIFO)原則。

程式碼實現

struct queue {
  int data[101];
  int head;
  int tail;
};

概念

棧是一種後進先出的資料結構。限定為只能在一段進行插入和刪除的操作。

舉例說明

比如說有一個小桶,小桶的直徑只能放置一個小球,現在小桶內依次放入2、1、3 號小球。假如現在需要拿出 2 號小球,那就必須先將 3 號小球和 1 號小球拿出來,最後才能拿到 2 號小球。在取小球的過程中,最先放進去的最後才能拿出來,最後放進去的卻可以最先拿出來。

連結串列

解決問題

在儲存一大波數的時候,通常使用陣列,但有時候陣列顯得不夠靈活,比如:

有一串已經從小到大排序好的數。現在需要往這竄數種插入一個數字使其得到的新序列仍符合從小到大排列。

動態儲存

例如在程式中儲存一個整數 10,除了使用 int a 這種方式在記憶體中申請一塊區域來儲存,還有另外一種動態儲存方法:

malloc(4);

不確定型別是多少個位元組的話,可以使用 sizeof() 來獲取,例如:

malloc(sizeof(int));

程式碼實現

每一個結點由兩部分組成。一部分用來存放具體的數值,另外一個部分需要用來儲存下一個結點的地址。例如:

// 連結串列結點
struct node {
  int data;
  struct node *next;
};
#include <stdio.h>
#include <stdlib.h>

// 連結串列結點
struct node {
  int data;
  struct node *next;
};

void insert(struct node *head, int n) {
  struct node *p, *t;
  
  t = head;

  while (t != NULL) {
    if (t->next == NULL || t->next->data > n) {
      // 如果當前結點是最後一個節點或者下一個結點的值大於待插入數的時候插入
      p = (struct node*)malloc(sizeof(struct node));
      p->data = n;
      p->next = t->next;
      t->next = p;
      break;
    }

    t = t->next;
  }
}

int main(void) {
  struct node *head, *p, *q, *t;
  int i, n, a;
  
  scanf("%d", &n);

  // 初始化頭指標為空
  head = NULL;

  for (i = 0; i < n; i++) {
    scanf("%d", &a);

    // 動態申請一個空間,用來存放一個結點,並且用臨時指標 p 指向這個結點
    p = (struct node *)malloc(sizeof(struct node));
    p->data = a;
    p->next = NULL;

    if (head != NULL) {
      // 頭指標不為空,則說明已經有結點了,插入那個結點的尾部
      q->next = p;
    } else {
      // 頭指標為空,則說明沒有結點,頭指標指向結點
      head = p;
    }

    // 替換之前的結點
    // q 用於存放當前結點
    q = p;
  }

  scanf("%d", &n);

  insert(head, n);
  
  t = head;

  while (t != NULL) {
    printf("%d ", t->data);
    t = t->next;
  }

  printf("
");

  return 0;
}

陣列實現-模擬連結串列

實現方式

申明兩個變數,分別是整數陣列 data 和整數陣列 right。data 用來存放序列種具體的數字,right 用來存放當前序列種每一個元素右邊在陣列 data 中位置,沒有則為 0。

程式碼實現

#include <stdio.h>
#define NUM 100

int main(void) {
  int data[NUM], right[NUM], i, n, len = 1;

  for (i = 0; i < NUM; i++) {
    data[i] = 0;
    right[i] = 0;
  }

  scanf("%d", &n);

  for (i = 1; i <= n; i++) {
    scanf("%d", &data[i]);

    right[len] = len + 1;
    len++;
  }

  // 插入
  scanf("%d", &data[len]);

  i = 1;

  while (i != 0) {
    if (data[right[i]] > data[len]) {
      // 如果當前結點下一個結點的值大於待插入數,將數插入到中間
      right[len] = right[i];ight[len] = right[i];
      right[i] = len;
      break;
    }
    i = right[i];
  }

  i = 1;

  while (i != 0) {
    printf("%d ", data[i]);
    i = right[i];
  }

  printf("
");

  return 0;
}

紙牌遊戲-小貓釣魚

#include <stdio.h>

// 佇列
struct queue {
  int data[101];
  int head;
  int tail;
} q1, q2;

// 棧
struct stack {
  int data[101];
  int top;
} cards;

// 模擬手牌
int c1[101] = {2, 4, 1, 2, 5, 6};
int c2[101] = {3, 1, 3, 5, 6, 4};


// 第一種做法
void play(struct queue *q1) {
  // 出牌
  int i, flag = 0, t = q1->data[q1->head];
  q1->head++;

  // 匹配
  for (i = 0; i < cards.top; i++) {
    if (cards.data[i] == t) {
      flag = 1;
      break;
    }
  }

  if (flag == 1) {
    // 匹配成功
    while (cards.data[cards.top - 1] != t) {
      q1->data[q1->tail++] = cards.data[cards.top - 1];
      cards.top--;
    }

    q1->data[q1->tail++] = t;
  } else {
    // 匹配失敗
    // 把打出的牌放入牌堆
    cards.data[cards.top++] = t;
  }
}

// 第二種

int main(void) {
  // 初始化牌堆
  cards.top = 0;
  // 初始化選手
  int i;
  q1.head = 0;
  q1.tail = 0;
  q2.head = 0;
  q2.tail = 0;
  for (i = 0; i < 6; i++) {
    q1.data[i] = c1[i];
    q1.tail++;
    q2.data[i] = c2[i];
    q2.tail++;    
  }

  while (q1.head < q1.tail && q2.head < q2.tail) {
    play(&q1);
    play(&q2);
  }

  if (q1.head == q1.tail) {
    printf("winer is q2
");
  } else {
    printf("winer is q1
");    
  }

  printf("q1:");
  for (i = q1.head; i < q1.tail; i++) {
    printf("%d ", q1.data[i]);
  }
  printf("
q2:");  
  for (i = q2.head; i < q2.tail; i++) {
    printf("%d ", q2.data[i]);
  }
  printf("
");  
  for (i = 0; i < cards.top; i++) {
    printf("%d ", cards.data[i]);
  }

  return 0;
}

括號匹配

題目

輸入一行只包含“()[]{}”的字串,請判斷形如“{[{}()]}”或者“{()[]{}}”的是否可以正確匹配。顯然上面兩個例子都是可以正確匹配的。“([)]”是不能匹配的。

程式碼實現

#include <stdio.h>
#include <string.h>

int main(void) {
  char left_d = `{`;
  char left_z = `[`;
  char left_x = `(`;
  char right_d = `}`;
  char right_z = `]`;
  char right_x = `)`;
  int flag = 1;
  int have_other_word = 0;
  int i;

  // 佇列
  char que[100];
  int head = 0;
  int tail = 0;

  // 棧
  struct Stack {
    int s[100];
    int top;
  } stack;
  stack.top = 0;

  gets(que);

  tail = strlen(que);

  for (i = 0; i < tail; i++) {
    if (
      que[i] != left_d &&
      que[i] != left_z &&
      que[i] != left_x &&
      que[i] != right_d &&
      que[i] != right_z &&
      que[i] != right_x
    ) {
      have_other_word = 1;
      break;
    }
  }

  if (have_other_word == 0) {
    while (head < tail) {
      if (que[head] == left_d || que[head] == left_z || que[head] == left_x) {
        stack.s[stack.top] = head;    
        stack.top++;
      } else {
        if (que[head] == right_d) {
          if (que[stack.s[stack.top - 1]] == left_d) {
            stack.top--;
          } else {
            flag = 0;
          }
        }
        
        if (que[head] == right_z) {
          if (que[stack.s[stack.top - 1]] == left_z) {
            stack.top--;        
          } else {
            flag = 0;        
          }
        }

        if (que[head] == right_x) {
          if (que[stack.s[stack.top - 1]] == left_x) {
            stack.top--;        
          } else {
            flag = 0;
          }
        }
      }

      if (flag == 0) {
        break;
      }

      head++;
    }
  } else {
    flag = 0;
  }

  if (flag) {
    printf("YES
");
  } else {
    printf("NO
");
  }

  return 0;
}

相關文章