一、写在前面
- 这一次包含的极其简单的数据结构有栈(载体为土豪送礼物的故事)、栈函数、队列(naive的循环队列)、链表(然而没有删除操作,还是一个待填的坑)。
- 由此可见这些数据结构的功能还远没有完善,算法的效率也不高,这些都是大坑…需要参考前辈给出的答案再修改不过这都是填坑的后话了。
二、栈
题目要求就是一个栈一样的口袋收纳(输入0)和分发(输入1)礼物。收纳礼物,分发加人名。
#include <stdio.h>
#include <string.h>
int main(void){
char present[50][10000];
char name[50][10000];
char presentf[50][10000];
int num=0;
int check=0;
int xun=0;
int precount=0;
int namecount=0;
scanf("%d",&num);
for(xun=0;xun<num;xun++){
scanf("%d",&check);
if(check==1){
scanf("%s",&present[precount]);
precount++;
}
else if(check==2){
scanf("%s",&name[namecount]);
strcpy(presentf[namecount],present[precount-1]);
namecount++;
precount--;
}
}
for(xun=0;xun<num/2;xun++){
printf("%s\0",name[xun]);
printf(" ");
printf("%s\0",presentf[xun]);
printf("\n");
}
return 0;
}
- 形成数据结构栈,用来控制数据的额外的参数就是栈顶指针。他的操作包含栈顶指针的移动,还有判断栈为满还是空(这里最讨厌的就是要对应好边界不然很容易越界)。这个代码非常简单啊不懂要说啥了…
三、栈函数
#define STACK_MAX_SIZE 6
int stack_push(int *stack, int num);
int stack_pop(int *stack);
int stack_top(int *stack, int *ret);
int *stack_constructor(void);
void stack_destructor(int *stack);
#define STACK_MAX_SIZE_EX 7
int *stack_constructor(void){
int * ptr;
ptr=(int *)malloc(sizeof(int)*STACK_MAX_SIZE_EX);
*ptr=1;
return ptr;
}
void stack_destructor(int *stack){
free(stack);
return;
}
int stack_top(int *stack, int *ret){
if(stack[0]==1)
return 0;
else{
*ret=stack[stack[0]-1];
return 1;
}
}
int stack_push(int *stack, int num){
if(stack[0]==7)
return 0;
else {
stack[stack[0]]=num;
stack[0]++;
return 1;
}
}
int stack_pop(int *stack){
if(stack[0]==1)
return 0;
else {
stack[0]--;
return 1;
}
}
- 栈的操作:创建栈,压入,弹出,取栈顶值,清空内存。
- 创建栈:如果创建多个栈的话,把栈顶指针整合到stack数组里面可以避免使用全局变量。而且可以非常方便的表示栈顶的数(stack[stack[0]])。
- 清空内存:为了清空申请出的空间所以整个栈的头指针不能变化呢…所以才要用到栈顶指针吧。
- 压入:首先要判断栈是否满了。压入数值之后要移动指针(即stack[0]1++),把栈顶指针一起整合到整个数组中真的非常方便呢。
- 弹出:首先判断栈是否是空的。这里的思想大概是“伪装”删除数据的样子,下移指针之后顶上的数据就不会被读取,就如同被删除了一般。
- 取栈顶:判断栈是否为空,然后就利用指针改值即可。
- 如同一中所说,判断边界是很重要的,这份代码指针指向的是“空的可以放东西的栈顶”,所以判断栈的条件就是空1满7。如果指向的是已经放好东西的栈顶,就应该是空0满6,总之整份代码逻辑要相同。
四、队列
#include <stdio.h>
int main(void){
int head=0, tail=0, distance=0, act=0, inp=0;
int arry[10];
while((act=getchar())!=EOF){
if(act=='1'){
scanf("%d",&inp);
if(distance==10){
printf("Push Failed. The queue is full.\n");
}else{
arry[head]=inp;
head++;
distance++;
if(head==10)
head=0;}
}else if(act=='0'){
if(distance==0){
printf("Pop Failed. The queue is empty.\n");
}else{
printf("%d\n",arry[tail]);
tail++;
distance--;
if(tail==10)
tail=0; }
}
}
return 0;
}
- 让队列成为队列的额外的管理的参数是数据首尾的距离。
- 首尾距离:用来判断队列为满还是为空。
- 头指针和尾指针:头指针用来表示下一个弹出的数,尾指针用来加入输入的数。区别于首尾距离的是他们是具体的数组的下标,对队列的满和空是不知情的。
呃…头指针和尾指针需要在数组内循环,可以用if条件判断是否越界,然而简单的做法是指针%数组长。强…- 这里的指针移动也做到了“伪装删除”的效果。
五、链表
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct node {
int value;
struct node* next;
} node ;
int size=0; // the size of linked list
node* head; // the head of linkedlist
//insert the value to the right position
//if the position is not valid, return false
//if insert successfully, return true
bool insert(int position, int value);
// return the value in the given position
int get(int position);
//clear the linkedlist, remember to free the memory you allocated
void clear();
//#include "linkedList.h"
void print() {
int i=0;
for (i = 0; i < size; ++i) {
printf("%d ", get(i));
}
printf("\n");
}
int main() {
head = NULL;
size = 0;
int n, i, position, value;
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d%d", &position, &value);
if (insert(position, value)) {
print();
} else {
printf("position is not valid\n");
}
}
clear();
return 0;
}
bool insert(int position, int value){
int count=0;
node *insert_node;
node *former_node=head;
if(position<0 || position>size){
return false;
}
if(position==0){
insert_node=(node *)malloc(sizeof(node));
insert_node->next=head;
insert_node->value=value;
head=insert_node;
size++;
return true;
}
if(position==size){
insert_node=(node *)malloc(sizeof(node));
insert_node->value=value;
insert_node->next=NULL;
for(count=0;count<position-1;count++){
former_node=former_node->next;
}
former_node->next=insert_node;
size++;
return true;
}else{
for(count=0;count<position-1;count++){
former_node=former_node->next;
}
insert_node=(node *)malloc(sizeof(node));
insert_node->value=value;
insert_node->next=former_node->next;
former_node->next=insert_node;
size++;
return true;
}
}
int get(int position){
int count=0;
node *num=head;
if(position==0){
return head->value;
}
for(count=0;count<position;count++){
num=num->next;
}
return num->value;
}
void clear(){
int count=0;
node *go=head;
node *save;
for(count=0;count<size;count++){
save=go->next;
free(go);
go=save;
}
return;
}
- 一个要填的坑:没有删除数据的函数。
- 链表由结构完成:数据域+结构域。数据域就是正常的填入和输出数据就好了。而结构域就是形式上串联起整个链表的指针:指向下一个结构。
- 读取函数:比数组麻烦而且整个链表在->next的过程中一定要想好循环多少次…边界问题…数据结构的家常便饭啊。这里要判断是否读第一个数据的问题。
- 清空函数:想不出别的方法只能用两个变量循环存储再删除了…不然总会丢失下一个地址free就会失败。
- 插入数据的函数:重头戏。先要判断是否插入的是头,尾。
插入头:把head存到自己的指向下一个的指针中,更改head指针为自己。
插入尾:更改原尾指针,并且把自己的下一个指针指向NULL。
插入中间:整个过程为:开辟新内存→存入值,把指针指向原本的下一个值→再把前一个指针指向自己。如果后两步做反了的话就会丢失下一个值的地址然后断链。插入头的时候指针的更改也是需要遵循这样的逻辑:先拿到下一个,在连接上一个。- size参数:记录链表长度的全局参数(不然…?)。插入的时候的重要依据,在插入函数里面要对其进行增减。