《啊哈!算法》-第 2 章:栈、队列、链表

队列

概念

队列是一种特殊的线性结构,它只允许在队列的首部(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("\n");

  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("\n");

  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\n");
  } else {
    printf("winer is q1\n");    
  }

  printf("q1:");
  for (i = q1.head; i < q1.tail; i++) {
    printf("%d ", q1.data[i]);
  }
  printf("\nq2:");  
  for (i = q2.head; i < q2.tail; i++) {
    printf("%d ", q2.data[i]);
  }
  printf("\n");  
  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\n");
  } else {
    printf("NO\n");
  }

  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值