虽然写这个博客主要目的是为了给我自己做一个思路记忆录,但是如果你恰好点了进来,那么先对你说一声欢迎。我并不是什么大触,只是一个菜菜的学生,如果您发现了什么错误或者您对于某些地方有更好的意见,非常欢迎您的斧正!
10.1栈和队列
栈(stack)又名堆栈,遵循后进先出原则。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈这一块我看来大概就是要学习这几个函数吧:
①初始化一个空栈InitStack() 使top=0
②判断栈是否非空StackEmpty() 判断top=0?
③入栈Push()
1、top+1
2、把新的元素赋值给arr[top]
④出栈Pop() 1、top-1 2、返回arr[top+1]
因为突然忘记了结构体这一块,我补一下,你要是学的比较好,可以跳过这里。摘抄自这篇文章(https://blog.csdn.net/cynophile/article/details/79062494)
声明了一个结构体 struct node,如果需要声明一个它的对象,则可以这样:struct node n1;
相当于把代码改名为Node了。以前需要这样声明”struct node n1;”,现在只需要”Node n1;”。
在结构体中声明结构体变量。
以下摘自(http://bbs.21ic.com/icview-1635522-1-1.html)
接下来是代码部分(建议复制后自己运行一下):
栈.h
#pragma once
typedef int dataType;
#define NUMBER_COUNT 10 /*栈里最多可以放的元素个数*/
struct Stack
{
int top;
dataType arr[NUMBER_COUNT];
};
/*①初始化一个空栈InitStack()*/
void InitStack(Stack *stack);
/*②判断栈是否非空StackEmpty()*/
bool StackEmpty(Stack *stack);
/*③入栈Push()*/
void Push(Stack *stack,dataType data);
/*④出栈Pop()*/
dataType Pop(Stack *stack);
/*栈的测试函数*/
void Teststack();
栈.cpp
#include "栈.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
/*①初始化一个空栈InitStack()*/
void InitStack(Stack *stack)
{
(*stack).top = 0;/*top为0就表示空栈*/
}
/*②判断栈是否非空StackEmpty()*/
bool StackEmpty(Stack *stack)
{
if (0 == (*stack).top)
return true;
else return false;
}
/*③入栈Push()*/
void Push(Stack *stack, dataType data)
{
if ((*stack).top == NUMBER_COUNT-1)
cout << "栈已满!" << endl;
else
{
(*stack).top++;/*因为多了一位,顶部下标加一*/
(*stack).arr[(*stack).top] = data;
}
}
/*④出栈Pop()*/
dataType Pop(Stack *stack)
{
if (StackEmpty(stack))
{
cout << "栈是空的!" << endl;
return 0;
}
else
{
(*stack).top--;
return (*stack).arr[(*stack).top + 1];
}
}
/*栈的测试函数*/
void Teststack()
{
struct Stack stack;
InitStack(&stack);/*初始化stack*/
srand((unsigned)time(NULL));/*建立随机种子*/
cout << "判断栈是否非空StackEmpty的测试:" << endl;
if (StackEmpty(&stack))cout << "Yes,it's empty!" << endl;
else cout << "No,it's not empty!" << endl;
cout << endl;
/*测试一下入栈操作*/
int i;
cout << "测试入栈Push,我们随便弄5个数字进去" << endl;
for (i = 1; i <= 5; i++)
{
Push(&stack, rand() % 100 + 1);/*rand() % 100 + 1是1-100之间的随机数*/
}
cout << endl;
cout << "我们来打印一下这个栈目前的数字情况" << endl;
for (i = 1; i <= stack.top; i++)
cout << stack.arr[i]<<" ";
cout << endl;
cout << endl;
/*测试一下出栈操作*/
cout << "测试出栈Push,我们就删除三个数字并把它们打印出来" << endl;
for (i = 0; i <3; i++)
cout << Pop(&stack) << " ";
}
主函数
#include <stdio.h>
#include "栈.h"
int main()
{
Teststack();
getchar();
return 0;
}
运行结果:
队列
是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。队列先进先出,就跟排队一样。我们来看一下队列有哪些要素。队列首先有对头head与队尾tail。然后是以下这些函数:
①初始化这个队列 InitQueue() 使head-1与tail 0
①判断队列是否为空QueueEmpty() head=-1与tail =0
②判断队列是否已满QueueFull() (tail+1)%总数=head
③入队EnQueue()
1、如果队列为空,arr[0]=data,并使head=0
2、如果队列非空,tail+1,arr[tail]=data
④出队DeQueue()
1、如果只有一个数据(head=tail),返回这个数据,初始化数组
2、如果有多个数据,返回arr[head],并使head+1
接下来是代码部分(建议复制后自己运行一下):
队列.h
#pragma once
typedef int dataType;
#define MAX_QUEUE_SIZE 10
struct Queue
{
int head;
int tail;
dataType arr[MAX_QUEUE_SIZE];
};
/*初始化这个队列,使head与tail指向0*/
void InitQueue(Queue *queue);
/*①判断队列是否为空QueueEmpty() head与tail都指向0*/
bool QueueEmpty(Queue *queue);
/*②判断队列是否已满QueueFull() (tail + 1) % 总数 = head*/
bool QueueFull(Queue *queue);
/*③入队EnQueue()*/
void EnQueue(Queue *queue,dataType data);
/*④出队DeQueue()*/
dataType DeQueue(Queue *queue);
/*队列测试函数*/
void TestQueue();
队列.cpp
#include "队列.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
/*初始化这个队列*/
void InitQueue(Queue *queue)
{
(*queue).head = -1;
(*queue).tail = 0;
}
/*①判断队列是否为空QueueEmpty() head与tail都指向0*/
bool QueueEmpty(Queue *queue)
{
if (((*queue).head == -1) && ((*queue).tail == 0))
return true;
else return false;
}
/*②判断队列是否已满QueueFull() (tail + 1) % 总数 = head*/
bool QueueFull(Queue *queue)
{
if ((*queue).head == ((*queue).tail + 1) % MAX_QUEUE_SIZE)
return true;
else return false;
}
/*③入队EnQueue()*/
void EnQueue(Queue *queue, dataType data)
{
if (QueueFull(queue))/*如果队列已满*/
{
cout << "这个队列已经满了!" << endl;
return;
}
else
{
if (QueueEmpty(queue))
{
(*queue).arr[0] = data;
(*queue).head = 0;
}
else
{
/*使tail向后进一步,若是arr[MAX_QUEUE_SIZE-1]已经有了,则放到arr[0]*/
(*queue).tail = ((*queue).tail + 1) % MAX_QUEUE_SIZE;
(*queue).arr[(*queue).tail] = data;
}
}
}
/*④出队DeQueue()*/
dataType DeQueue(Queue *queue)
{
if (QueueEmpty(queue))/*判断是否为空*/
{
cout << "队列是空的!" << endl;
return 0;
}
else
{
int temp;
if ((*queue).head == (*queue).tail)/*表明只有一个数*/
{
/*直接返回这个数并且初始化队列*/
temp = (*queue).arr[(*queue).head];
InitQueue(queue);
return temp;
}
else/*如果不止一个数*/
{
/*返回head所在的数,head向前一步*/
temp = (*queue).arr[(*queue).head];
(*queue).head = ((*queue).head + 1) % MAX_QUEUE_SIZE;
return temp;
}
}
}
/*队列测试函数*/
void TestQueue()
{
srand((unsigned)time(NULL));/*建立随机种子*/
struct Queue queue;
int i;
InitQueue(&queue);/*初始化队列*/
cout << "①测试一下 判断非空 这个函数" << endl;
cout << "测试结果:";
if (QueueEmpty(&queue))cout << "It's empty!" << endl;
else cout << "It's not empty!" << endl;
cout << endl;
cout << "②测试一下 入队 这个函数" << endl;
cout << "我们插入8个数字后,我们的队列为:";
for (i = 0; i < 8; i++)/*插入*/
{
EnQueue(&queue, rand() % 100 + 1);
}
for (i = 0; i < 8; i++)/*输出*/
{
cout << queue.arr[i] << " ";
}
cout << endl;
cout << endl;
cout << "③测试一下 出队 这个函数" << endl;
cout << "取出三个数字并输出它们:";
for (i = 0; i < 3; i++)
{
cout << DeQueue(&queue) << " ";
}
cout << endl;
cout << endl;
cout << "④测试一下 对满 这个函数" << endl;
cout << "再插入5个数字后:" << endl;
for (i = 0; i < 5; i++)
{
EnQueue(&queue, rand() % 100 + 1);
}
i = queue.head;
do
{
if (i == MAX_QUEUE_SIZE)i = 0;
cout << queue.arr[i] << " ";
i++;
} while (i != queue.tail+1);
cout << endl;
if (QueueFull(&queue))cout << "It's full!" << endl;
else cout << "It's not full!" << endl;
}
主函数
#include <stdio.h>
#include "队列.h"
int main()
{
TestQueue();
getchar();
return 0;
}
运行结果:
10.2链表
没有哨兵的双向链表
①链表的搜索ListSearch()
1、建立一个指向链表表头的指针x
2、只要x非空并且x的key与要搜索的num不等,就让x指向下一位
3、返回这个x
②链表的插入ListInsert()
1、让x的下一位指向链表的表头
2、如果链表非空,就把表头的prev指针指向x
3、把x赋给链表的表头,并使x的prev指针指向NULL
③链表的删除ListDelete()
1、如果x不是表头,就让x的前一位指向x的后一位
2、如果x是表头,就让x的后一位成为表头
3、如果x不是表尾,就让x的后一位指向x的前一位
④打印列表ListPrint()
1、新建一个指针p指向链表表头
2、只要p->next非空,就打印p->key,并使p指向p->next
接下来是代码部分(建议复制后自己运行一下):
没有哨兵的双向链表.h
#pragma once
#define LEN sizeof(struct List)
struct List
{
struct List *prev; /*指向前一个结点*/
struct List *next; /*指向后一个结点*/
struct List *head;
int key;
};
/*查找数据*/
struct List* ListSearch(struct List list, int num);
/*链表的插入*/
struct List* ListInsert(struct List list, struct List *x);
/*链表的删除*/
struct List* ListDelete(struct List list, struct List *x);
/*打印列表*/
void ListPrint(struct List list);
/*链表测试函数*/
void ListTest();
没有哨兵的双向链表.cpp
#include "没有哨兵的双向链表.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
/*查找数据*/
struct List* ListSearch(struct List list, int num)
{
struct List *x;
x = list.head;
while (x != NULL && x->key != num)/*x非空并且x的key与要搜索的num不等*/
{
x = x->next;
}
return x;
}
/*链表的插入*/
struct List* ListInsert(struct List list, struct List *x)
{
x->next = list.head;
if (list.head != NULL)/*如果链表非空*/
{
list.head->prev = x;/*把x赋给链表的第一位*/
}
list.head = x;
x->prev = NULL;/*x前一个结点为空*/
return x;
}
/*链表的删除*/
struct List* ListDelete(struct List list, struct List *x)
{
if (x->prev != NULL)/*x不是表头*/
{
x->prev->next = x->next;/*让x的前一位指向x的后一位*/
}
else/*x是表头*/
{
list.head = x->next;/*让x的下一位成为表头*/
}
if (x->next != NULL)/*x不是表尾*/
{
x->next->prev = x->prev;/*让x的后一位指向x的前一位*/
}
return list.head;
}
/*打印列表*/
void ListPrint(struct List list)
{
struct List *p = list.head;/*p是一个指向表头的指针*/
while (p)/*只要p不指向NULL*/
{
cout << p->key << " ";/*就打印p的数据*/
p = p->next;/*让p指向它的下一位*/
}
cout << endl;
cout << endl;
}
/*链表测试函数*/
void ListTest()
{
struct List list, *x, *t;/*建立一个链表,和两个指针*/
int i;
srand((unsigned)time(NULL));/*建立随机种子*/
list.head = NULL; x = NULL; t = NULL;
for (i = 0; i < 10; i++)
{
x = (struct List*)malloc(LEN);/*开辟一个新的结点*/
x->key = rand() % 100 + 1;/*x->key的值在1-100之间*/
if (i == 6)
t = x;/*t为链表第7个结点*/
list.head = ListInsert(list, x);/*把x依次插入到链表中*/
}
cout << "打印链表:";
ListPrint(list);
x = ListSearch(list, 60);/*x是60所在的结点*/
if (x == NULL)
cout << "Can't find the data!" << endl;
else
cout << "The number 60 has been found!" << endl;
cout << endl;
cout << "我们现在删除第7个进入的元素(也就是第四个):" << endl;
ListDelete(list, t);
cout << "打印链表:";
ListPrint(list);
}
主函数
#include <stdio.h>
#include "没有哨兵的双向链表.h"
int main()
{
ListTest();
getchar();
return 0;
}
运行结果:
有哨兵的双向链表
**哨兵**是一个哑对象,其作用是简化边界处理条件。我对哨兵的作用其实并不是特别特别清除,我也为此查找了一些网上的文章,但是感觉要么看不懂,要么模棱两可,这样我就干脆只记住:哨兵就是一个使我们的代码更简洁、更加容易理解的存在,就好了。
我们来看一下书上对哨兵的图示:
按照书中对它的描述,我大概也画了一幅关系图:
一开始并看不懂struct NilList* InsertList(struct NilList &list, struct NilList *x)这里的那个&list,于是我查了一下,这是一个引用传递。引用传递是将变量的内存地址传递给方法,方法操作变量时会找到保存在该地址的变量,对其进行操作。会对原变量造成影响。
有哨兵的双向链表.h
#pragma once
#define NILLEN sizeof(struct NilList)
struct NilList
{
struct NilList *prev; /*指向前一个结点*/
struct NilList *next; /*指向后一个结点*/
struct NilList *nil; /*哨兵*/
int key;
};
/*初始化链表*/
struct NilList* InitList(struct NilList &list);
/*查找数据*/
struct NilList* SearchList(struct NilList &list, int num);
/*链表的插入*/
struct NilList* InsertList(struct NilList &list, struct NilList *x);
/*链表的删除*/
struct NilList* DeleteList(struct NilList &list, struct NilList *x);
/*打印列表*/
void PrintList(struct NilList list);
/*链表测试函数*/
void TestList();
有哨兵的双向链表.cpp
#include "有哨兵的双向链表.h"
#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;
/*初始化链表*/
struct NilList* InitList(struct NilList &list)
{
list.nil = (struct NilList*)malloc(NILLEN);/*为哨兵开辟一个结点*/
list.nil->next = list.nil;/*让哨兵的尾指针指向本身*/
list.nil->prev = list.nil;/*让哨兵的头指针指向本身*/
return list.nil;
}
/*查找数据*/
struct NilList* SearchList(struct NilList &list, int num)
{
struct NilList *x;
x = list.nil->next;/*让x指向链表头部*/
while (x != list.nil && x->key!=num)
{
x = x->next;
}
return x;
}
/*链表的插入*/
struct NilList* InsertList(struct NilList &list, struct NilList *x)
{
x->next = list.nil->next;
list.nil->next->prev = x;/*哨兵->next(表头x)->prev->哨兵*/
list.nil->next = x;/*哨兵->next(表头x)*/
x->prev = list.nil;/*(表头x)->prev->哨兵*/
return list.nil;
}
/*链表的删除*/
struct NilList* DeleteList(struct NilList &list, struct NilList *x)
{
x->next->prev = x->prev;/*x的后一位指向x的前一位*/
x->prev->next = x->next;/*x的前一位指向x的后一位*/
return list.nil;
}
/*打印列表*/
void PrintList(struct NilList list)
{
struct NilList *p = list.nil->next;
if (p != list.nil)
{
while (p != list.nil)
{
cout << p->key << " ";
p = p->next;
}
cout << endl;
}
else
{
cout << "It's empty!" << endl;
}
cout << endl;
}
/*链表测试函数*/
void TestList()
{
struct NilList list, *x, *t;/*建立一个链表,和两个指针*/
int i;
InitList(list);/*初始化这个链表*/
PrintList(list);
srand((unsigned)time(NULL));
x = NULL; t = NULL;
for (i = 0; i < 10; i++)
{
x = (struct NilList*)malloc(NILLEN);/*开辟一个新的结点*/
x->key = rand() % 100 + 1;/*x->key的值在1-100之间*/
if (i == 6)
t = x;/*t为链表第7个结点*/
list.nil=InsertList(list, x);/*把x依次插入到链表中*/
}
cout << "打印链表:";
PrintList(list);
x = SearchList(list, 60);/*x是60所在的结点*/
if (x ==list.nil)
cout << "Can't find the data!" << endl;
else
cout << "The number 60 has been found!" << endl;
cout << endl;
cout << "我们现在删除第7个进入的元素(也就是第四个):" << endl;
DeleteList(list, t);
cout << "打印链表:";
PrintList(list);
}
主函数
#include <stdio.h>
#include "有哨兵的双向链表.h"
int main()
{
TestList();
getchar();
return 0;
}
运行结果:
10.3指针和对象的实现
当有些语言不支持指针和对象数据类型时,我们利用数组和数组下标构造对象和指针。这种链表称为 静态链表。10.4有根树的介绍
跟二叉树一样,二叉树是左儿子,右儿子,这个是左儿子右兄弟具体的我推荐一篇文章: Flammable_ice的文章(https://blog.csdn.net/z84616995z/article/details/19202773)写的非常好!参考网站以及博客:
百度百科数据结构(八)——栈
http://blog.51cto.com/9291927/2063393
算法导论第十章基本数据结构(写的很不错)
https://blog.csdn.net/z84616995z/article/details/19202773
c语言结构体struct相关使用说明
https://blog.csdn.net/cynophile/article/details/79062494