1.栈与队列
1.1 如何使用两个栈实现队列先进先出的功能
1.定义一个结构体,该结构体中包含两个栈的指针和一个用来表示当前队列长度的int型变量;
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int nValue;
struct node *pNext;
}Node;
typedef struct stack
{
Node *pTop;
int nCount;
}Stack;
typedef struct queue
{
Stack *pStack1;
Stack *pStack2;
int qCount
}Queue;
void s_Init(Stack **pStack)
{
*pStack = (Stack*)malloc(sizeof(Stack));
(*pStack)->pTop = NULL;
(*pStack)->nCount = 0;
}
void s_Push(Stack *pStack,int nNum)
{
if(pStack == NULL)
{
printf("栈不存在\n");
return;
}
Node *pTemp = NULL;
pTemp = (Node*)malloc(sizeof(Node));
pTemp->nValue = nNum;
pTemp->pNext = pStack->pTop;
pStack->pTop = pTemp;
pStack->nCount++;
}
int s_Pop(Stack *pStack)
{
if(pStack == NULL || pStack->nCount == 0)
{
printf("错误\n");
return -1;
}
int nNum;
Node *pDel = NULL;
pDel = pStack->pTop;
nNum = pDel->nValue;
pStack->pTop = pStack->pTop->pNext;
free(pDel);
pDel = NULL;
pStack->nCount--;
return nNum;
}
void s_Clear(Stack *pStack)
{
if(pStack == NULL)return;
while(pStack->nCount != 0)
{
s_Pop(pStack);
}
}
void s_Destroy(Stack **pStack)
{
s_Clear(*pStack);
free(*pStack);
*pStack = NULL;
}
Node *s_GetTop(Stack *pStack)
{
if(pStack == NULL)exit(1);
return pStack->pTop;
}
int s_GetCount(Stack *pStack)
{
if(pStack == NULL)exit(1);
return pStack->nCount;
}
int s_IsEmpty(Stack *pStack)
{
if(pStack == NULL)exit(1);
return pStack->nCount == 0 ?1:0;
}
//--------------------两个栈实现队列的功能---------------------------
void q_Init(Queue **pQueue)
{
*pQueue = (Queue*)malloc(sizeof(Queue));
(*pQueue)->pStack1 = NULL;
(*pQueue)->pStack2 = NULL;
(*pQueue)->qCount = 0;
s_Init(&((*pQueue)->pStack1));
s_Init(&((*pQueue)->pStack2));
}
void q_Push(Queue *pQueue,int nNum)
{
if(pQueue == NULL || pQueue->pStack1 == NULL || pQueue->pStack2 == NULL)return;
//栈1入队
//栈2 内若有元素 则将其放回栈1
while(pQueue->pStack2->nCount != 0)
{
s_Push(pQueue->pStack1,s_Pop(pQueue->pStack2));
}
//新来的元素入栈1
s_Push(pQueue->pStack1,nNum);
(*pQueue)->qCount++;
}
int q_Pop(Queue *pQueue)
{
if(pQueue == NULL || pQueue->pStack1 == NULL || pQueue->pStack2 == NULL ||
(pQueue->pStack1->nCount == 0 && pQueue->pStack2->nCount == 0))return -1;
//栈2 出队
//栈1有元素 则将元素依次放入栈2
while(pQueue->pStack1->nCount != 0)
{
s_Push(pQueue->pStack2,s_Pop(pQueue->pStack1));
}
int nNum;
nNum = s_Pop(pQueue->pStack2);
(*pQueue)->qCount--;
return nNum;
}
int main()
{
Queue *pQueue = NULL;
q_Init(&pQueue);
q_Push(pQueue,1);
q_Push(pQueue,2);
q_Push(pQueue,3);
q_Push(pQueue,4);
printf("%d\n",q_Pop(pQueue));
printf("%d\n",q_Pop(pQueue));
printf("%d\n",q_Pop(pQueue));
return 0;
}
2.我的思路是:用栈1来正着存放数组(先进去的元素在栈底部,后进去的在栈顶部),用栈2来存放反着的元素(先进去的元素在栈顶部,后进去的在栈底部),这样的话当我们要出栈时只用pop以下栈2即可达到目的;
1.2 如何使用两个队列来实现栈的内容
1.定义一个结构体,其中包括两个队列指针和一个int型变量;
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int nValue;
struct node *pNext;
}Node;
typedef struct queue
{
int nCount;
Node *pHead;
Node *pTail;
}Queue;
typedef struct stack
{
Queue *pQueue1;
Queue *pQueue2;
int nCount;
}Stack;
void q_Init(Queue **pQueue)
{
*pQueue = (Queue*)malloc(sizeof(Queue));
(*pQueue)->nCount = 0;
(*pQueue)->pHead = NULL;
(*pQueue)->pTail = NULL;
}
void q_Push(Queue *pQueue,int nNum)
{
if(pQueue == NULL)return;
Node *pTemp = NULL;
pTemp = (Node*)malloc(sizeof(Node));
pTemp->nValue = nNum;
pTemp->pNext = NULL;
//尾添加
if(pQueue->pHead == NULL)
{
pQueue->pHead = pTemp;
}
else
{
pQueue->pTail->pNext = pTemp;
}
pQueue->pTail = pTemp;
pQueue->nCount++;
}
int q_Pop(Queue *pQueue)
{
if(pQueue == NULL || pQueue->nCount == 0)return -1;
int nNum;
Node *pDel = NULL;
pDel = pQueue->pHead;
nNum = pDel->nValue;
pQueue->pHead = pQueue->pHead->pNext;
free(pDel);
pDel = NULL;
pQueue->nCount--;
if(pQueue->nCount == 0)
{
pQueue->pTail = NULL;
}
return nNum;
}
int q_IsEmpty(Queue *pQueue)
{
if(pQueue== NULL)exit(1);
return pQueue->nCount == 0?1:0;
}
//----------------------------------
void s_Init(Stack **pStack)
{
*pStack = (Stack*)malloc(sizeof(Stack));
(*pStack)->nCount = 0;
(*pStack)->pQueue1 = NULL;
(*pStack)->pQueue2 = NULL;
q_Init(&((*pStack)->pQueue1));
q_Init(&((*pStack)->pQueue2));
}
void s_Push(Stack *pStack,int nNum)
{
if(pStack == NULL || pStack->pQueue1 == NULL || pStack->pQueue2 == NULL)return;
if(pStack->pQueue1->nCount != 0)
{
q_Push(pStack->pQueue1,nNum);
}
else
{
q_Push(pStack->pQueue2,nNum);
}
pStack->nCount++;
}
int s_Pop(Stack *pStack)
{
if(pStack == NULL || pStack->pQueue1 == NULL || pStack->pQueue2 == NULL || pStack->nCount == 0)return -1;
int nNum;
if(!q_IsEmpty(pStack->pQueue1))
{
//队列1的元素依次压入队2
while(pStack->pQueue1->nCount > 1)
{
q_Push(pStack->pQueue2,q_Pop(pStack->pQueue1));
}
nNum = q_Pop(pStack->pQueue1);
}
else
{
while(pStack->pQueue2->nCount > 1)
{
q_Push(pStack->pQueue1,q_Pop(pStack->pQueue2));
}
nNum = q_Pop(pStack->pQueue2);
}
pStack->nCount--;
return nNum;
}
int main()
{
Stack *pStack = NULL;
s_Init(&pStack);
s_Push(pStack,1);
s_Push(pStack,2);
s_Push(pStack,3);
s_Push(pStack,4);
printf("%d\n",s_Pop(pStack));
printf("%d\n",s_Pop(pStack));
printf("%d\n",s_Pop(pStack));
return 0;
}
2.这里我的思路是:在push的时候queue1与queue2中必有一个是空的,那么我将新压入的元素放到非空的队列的尾部;在pop过程中,我们找出不为空的队列,将该队列除了尾部的元素以外的所有元素压入到空的队列中,在将只剩尾部的队列pop掉,作为返回值返回;
2.树
2.1 树,也成树状图,是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
2.2 一棵树必然包括:根节点、叶子节点、中间节点、边、路径(是指从根节点到叶子节点的路径)、树的层数(从1开始)、树的高度(也就是树的层数)、某个节点的高度(表示以改节点为根节点的树的层数)、深度
2.3 一棵树可能包括:权值、带权节点
2.4 二叉树
1.每个节点最多只含有两个孩子节点;
2.子树、左子树、右子树
3.度:父节点孩子的个数
2.5 特殊的二叉树
1.满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树;
2.完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树(或者说当只有最后一层有空缺,并且从右向左空缺的树);
3.排序二叉树(二叉搜索树)BST:所有左子树的元素都比父节点小,所有右子树的元素都比父节点大,且其中每个节点都是唯一存在的,没有相同的值;
4.平衡二叉树(AVL树):必须以是一颗BST为前提条件,树中任意节点左右子树的高度差不超过1;
5.B-Tree(B树):是一个节点可以拥有多于2个子节点的二叉查找树;
1).根节点至少有两个子节点;
2).每个节点有M-1个key,并且以升序排列;
3).位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间;
4).其它节点至少有M/2个子节点;
6.B+树:B+树是对B树的一种变形树,它与B树的差异在于:
1).有k个子结点的结点必然有k个关键码;
2).非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中;
3).树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录;
2.6 二叉树的性质
1.一个K层的二叉树,最多有二的K次方减1个节点(k>=1);
2.一个K层的二叉树最多有二的K减一次方个节点;
3.n0是度为0的节点的个数,n1是度为1的节点的个数,n2是度为2的节点的个数,总结点的个数S=n0+n1+n2,S=2*n2+n1,由此可得n0=n2+1;
4.一个有n个节点的完全二叉树,其高度为log以二为底的n并向下取整再加1;
5.把一颗完全二叉树按照从上到下从左到右的顺序从1开始进行编号,其中编号为i的节点若满足2i<=n,则说明该节点有左孩子,并且左孩子的编号为2i<=n;若满足2i+1<=n,则说明该节点有右孩子,并且右孩子的编号为2i+1<=n;
6.对于完全二叉树,父节点的编号范围为: 1~n/2 ;
7.从0开始编号的完全二叉树,编号为i的节点的左孩子编号为2i+1,右孩子编号为2i+2,父节点的编号范围为0~n/2+1。