链表
设单链表的表头指针为L,结点结构由data和next两个域构成,其中data域为字符型。试设计算法判断该链表的全部n个字符是否中心对称。例如xyx、xyyx都是中心对称。
分析:
这题完全可以参考19年那题,我们直接找到中间结点然后将后半段全部翻转,接着遍历一遍就好了(头指针和指向中间结点的那个指针同时向后直至后面那个指针指向空为止)唯一要额外考虑的是要是单链表长度为奇数时,要对正中间这个结点做点特殊处理,使其不参与后续的遍历比较。
cpp
struct Node {
char data;
Node* next;
};
bool isSymmetric(Node* head) {
if (!head || !head->next) return true; // 空链表或只有一个节点
Node *slow = head, *fast = head;
Node *prev = nullptr;
// 找到中间节点,反转后半段链表
while (fast && fast->next) {
fast = fast->next->next; // 快指针每次走两步
Node* nextNode = slow->next; // 保存下一个节点
slow->next = prev; // 反转当前节点的指针
prev = slow; // 移动前驱
slow = nextNode; // 移动到下一个节点
}
// 如果链表长度为奇数,跳过中间节点
if (fast) {
slow = slow->next; // 跳过中间节点
}
// 比较前半段与反转后的后半段
while (prev && slow) {
if (prev->data != slow->data) {
return false; // 不对称
}
prev = prev->next; // 移动前半段
slow = slow->next; // 移动后半段
}
return true; // 对称
}
当然还有一种做法还是借助数组,第一轮遍历时,将其数值复制到数组当中。接着利用数组可以随机存取的特点,再从中间结点向两端比对即可。
cpp
struct Node {
char data;
Node* next;
};
bool isSymmetric(Node* L) {
std::vector<char> chars;
Node* current = L;
// 遍历链表并存储字符
while (current != nullptr) {
chars.push_back(current->data);
current = current->next;
}
// 检查对称性
int n = chars.size();
for (int i = 0; i < n / 2; ++i) {
if (chars[i] != chars[n - 1 - i]) {
return false; // 不对称
}
}
return true; // 对称
}
树
链式结构体
cpp
typedef struct BiTNode{
int data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//BiTNode *p = BiTree p;
//BiTNode *p 偏向表示当前结点
//BiTree p 偏向表示当前结果为根的树
typedef struct LNode{
ElemType data;
struct LNode* next;
}LNode,*LinkList;
二叉树的五种基本形态
- 空树
- 只有一个结点
- 有左子树,但右子树为空
- 有右子树,但左子树为空
- 左右两子树都不为空
递归遍历
递归算法理解:重点关注函数的功能(递和归)
二叉树的先序、中序和后序遍历
cpp
void PreOrder(BiTree T){
if(T!=NULL){
visit(T); //访问根结点
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
}
}
void PreOrder(BiTree T){
if(T==NULL) return;
visit(T); //访问根结点
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
}
//另外两个也只是交换语句顺序而已
先序遍历中第n个结点的值
cpp
int n=0;
void findn(BiTree T,int cnt){
if(T==NULL) return;
n++;
if(n==cnt){
printf("%d",T->data);
}
findn(T->lchild,cnt);
findn(T->rchild,cnt);
}
求data域等于key的结点
cpp
BiTree* g=NULL; //全局变量
void findkey(BiTree T,int key){
if(T==NULL) return;
if(T->data==key) g=T; //g只会赋值一次
findkey(T->lchild,key);
findkey(T->rchild,Key);
}
求二叉树所有的结点个数
cpp
//Way1:使用全局变量
int n=0;//全局变量,表示总结点数
void cnt(BiTree T){
if(T==NULL) return;
n++;
cnt(T->lchild);
cnt(T->rchild);
}
//Way 2:使用树的五种结构
int cnt(BiTree T){
if(T==NULL) return 0;
return 1+cnt(T->lchild)+cnt(T->rchild);
}
求二叉树所有叶子结点个数
cpp
//Way1:使用全局变量
int n=0;//全局变量,表示总结点数
void cnt(BiTree T){
if(T==NULL) return;
if(T->lchild==NULL&&T->rchild==NULL){
n++;
}
cnt(T->lchild,k);
cnt(T->rchild,K);
}
//Way 2:使用树的五种结构
int cnt(BiTree T){
if(T==NULL) return 0;
else if(T->lchild==NULL&&T->rchild==NULL) return 1;
//else if(T->lchild!=NULL&&T->rchild==NULL) return cnt(T->lchild);
//else if(T->lchild==NULL&&T->rchild!=NULL) return cnt(T->rchild);
return cnt(T->lchild)+cnt(T->rchild);
}
求二叉树所有的单分支结点
cpp
int cnt(BiTree T){
if(T==NULL) return 0;
else if(T->lchild==NULL&&T->rchild==NULL) return 0;
else if(T->lchild!=NULL&&T->rchild==NULL) return 1+cnt(T->lchild);
else if(T->lchild==NULL&&T->rchild!=NULL) return 1+cnt(T->rchild);
return cnt(T->lchild)+cnt(T->rchild);
}
求二叉树所有的双分支结点
cpp
int cnt(BiTree T){
if(T==NULL) return 0;
else if(T->lchild==NULL&&T->rchild==NULL) return 0;
else if(T->lchild!=NULL&&T->rchild==NULL) return cnt(T->lchild);
else if(T->lchild==NULL&&T->rchild!=NULL) return cnt(T->rchild);
return 1+cnt(T->lchild)+cnt(T->rchild);
}
求二叉树的高度
cpp
int high(BiTree T){
if(T==NULL) return 0;
return max(high(T->rchild),high(T->lchild))+1;
}
判断两个二叉树是否相等
cpp
int same(BiTree a,BiTree b){
if(a==NULL&&b==NULL) return 1;
else if(a!=NULL&&b==NULL) return 0;
else if(a==NULL&&b!=NULL) return 0;
else{
if(a->data==b->data){
return same(a->lchild,b->lchild)&&same(a->rchild,b->rchild);
}else{
return 0;
}
}
判断两个二叉树是否相似
只要结构一样就好
cpp
int resemble(BiTree a,BiTree b){
if(a==NULL&&b==NULL) return 1;
else if(a==NULL||b==NULL) return 0;
else{
return resemble(a->lchild,b->lchild)&&resemble(a->rchild,b->rchild);
}
}
二叉树的层次遍历
cpp
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q;
InitQueue(Q); //初始化辅助队列
BiTree p;
EnQueue(Q,T); //将根节点入队
while(!isEmpty(Q)){ //队列不空则循环
DeQueue(Q,p); //队头结点出队
visit(p); //访问出队结点
if(p->lchild != NULL)
EnQueue(Q,p->lchild); //若左孩子不空,左孩子入队
if(p->rchild != NULL)
EnQueue(Q,p->rchild); //若右孩子不空,右孩子入队
}
}
二叉树的层次遍历------变体(反转)
cpp
// 反转层序遍历
void ReverseLevelOrder(BiTree T) {
LinkQueue Q;
InitQueue(Q); // 初始化辅助队列
EnQueue(Q, T); // 将根节点入队
LinkStack S;
InitStack(S);
BiTNode *p, *s;
while (!isEmpty(Q)) { // 队列不空则循环
DeQueue(Q, p); // 队头结点出队
Push(S, p);
if (p->lchild != NULL)
EnQueue(Q, p->lchild); // 若左孩子不空,左孩子入队
if (p->rchild != NULL)
EnQueue(Q, p->rchild); // 若右孩子不空,右孩子入队
}
while (!StackEmpty(S)) {
Pop(S, s);
printf("%d ", s->data); // 打印节点数据
free(s); // 释放节点内存
}
}
交换二叉树的左右子树
cpp
void swap(BiTree T){
if(T==NULL) return;
BiTNode* temp=T->lchild;
T->lchild=T->rchild;
T->rchild=temp;
swap(T->lchild);
swap(T->rchild);
}
层次遍历
cpp
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q;
InitQueue(Q); //初始化辅助队列
BiTree p;
EnQueue(Q,T); //将根节点入队
while(!isEmpty(Q)){ //队列不空则循环
DeQueue(Q,p); //队头结点出队
visit(p); //访问出队结点
if(p->lchild != NULL)
EnQueue(Q,p->lchild); //若左孩子不空,左孩子入队
if(p->rchild != NULL)
EnQueue(Q,p->rchild); //若右孩子不空,右孩子入队
}
}
求二叉树高度
cpp
int high(BiTree T) {
if (T == NULL) return 0; // 空树高度为0
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, T); // 将根节点入队
int size = 1; // 当前队列中的元素数量
int high = 0; // 树的高度
while (size != 0) {
int level_size = size; // 当前层的节点数
for (int i = 0; i < level_size; i++) {
BiTNode *cur = DeQueue(Q); // 队头结点出队
size--;
if (cur->lchild != NULL) {
EnQueue(Q, cur->lchild); // 若左孩子不空,左孩子入队
size++;
}
if (cur->rchild != NULL) {
EnQueue(Q, cur->rchild); // 若右孩子不空,右孩子入队
size++;
}
}
high++; // 每遍历完一层,高度加1
}
return high; // 返回二叉树的高度
}
求二叉树的宽度
cpp
int width(BiTree T) {
if (T == NULL) return 0; // 空树宽度为0
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, T); // 将根节点入队
int size = 1; // 当前队列中的元素数量
int width = 0;
while (size != 0) {
int level_size = size; // 当前层的节点数
width = max(level_size, width);
for (int i = 0; i < level_size; i++) {
BiTNode *cur = DeQueue(Q); // 队头结点出队
size--;
if (cur->lchild != NULL) {
EnQueue(Q, cur->lchild); // 若左孩子不空,左孩子入队
size++;
}
if (cur->rchild != NULL) {
EnQueue(Q, cur->rchild); // 若右孩子不空,右孩子入队
size++;
}
}
}
return width;
}
求data域等于key的结点的层次号
cpp
int keyLevel(BiTree T, int key) {
if (T == NULL) return -1; // 空树返回-1
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, T); // 将根节点入队
int size = 1; // 当前队列中的元素数量
int high = 0;
int ans = -1; // 未找到key时的标记
while (size != 0) {
int level_size = size; // 当前层的节点数
for (int i = 0; i < level_size; i++) {
BiTNode *cur = DeQueue(Q); // 队头结点出队
size--;
if (cur->data == key) ans = high + 1;
if (cur->lchild != NULL) {
EnQueue(Q, cur->lchild); // 若左孩子不空,左孩子入队
size++;
}
if (cur->rchild != NULL) {
EnQueue(Q, cur->rchild); // 若右孩子不空,右孩子入队
size++;
}
}
high++;
}
return ans; // 返回找到的层数,未找到返回-1
}
判断二叉树是否为完全二叉树
cpp
int IsCompleteBinaryTree(BiTree T) {
if (T == NULL) return 1; // 空树视为完全二叉树
LinkQueue Q;
InitQueue(Q);
EnQueue(Q, T); // 将根节点入队
while (!isEmpty(Q)) {
BiTNode *cur = DeQueue(Q);
if (cur != NULL) {
EnQueue(&Q, cur->lchild);
EnQueue(&Q, cur->rchild);
} else {
// 遇到空节点,检查下一个节点是否非空
if (!isEmpty(Q)) {
BiTNode *next = DeQueue(Q);
if (next != NULL) {
return 0; // 如果下一个节点非空,则不是完全二叉树
}
}
}
}
return 1; // 遍历完所有节点,是完全二叉树
}
非递归排序
二叉树的先序、中序和后序遍历
先序/中序(输出语句的改变决定其是先还是中):
- 循环结束条件:node为空且栈空
- node不空,入栈,node到左孩子
- node空,出栈,node到右孩子
后序:
- 循环结束条件:node为空且栈空
- node不空,入栈,node到左孩子
- node空取栈顶,看看右孩子,没有右孩子或者右孩子访问过,出栈标记,否则node到右孩子
注:因为每个结点都会被访问三次
cpp
// 先序遍历
void PreOrder(BiTree T) {
LinkStack S;
InitStack(S);
BiTNode *node = T;
while (node != NULL || !StackEmpty(S)) {
if (node != NULL) {
printf("%d ", node->data); // 访问节点
Push(S, node);
node = node->lchild;
} else {
node = Pop(S);
node = node->rchild;
}
}
}
// 中序遍历
void InOrder(BiTree T) {
LinkStack S;
InitStack(S);
BiTNode *node = T;
while (node != NULL || !StackEmpty(S)) {
if (node != NULL) {
Push(S, node);
node = node->lchild;
} else {
node = Pop(S);
printf("%d ", node->data); // 访问节点
node = node->rchild;
}
}
}
// 后序遍历
void PostOrder(BiTree T) {
LinkStack S;
InitStack(S);
BiTNode *node = T;
BiTNode *lastVisited = NULL;
while (node != NULL || !StackEmpty(S)) {
if (node != NULL) {
Push(S, node);
node = node->lchild;
} else {
BiTNode *topNode;
GetTop(S, topNode); // 获取栈顶元素
if (topNode->rchild == NULL || topNode->rchild == lastVisited) {
printf("%d ", topNode->data); // 访问节点
lastVisited = Pop(S); // 弹出节点
} else {
node = topNode->rchild;
}
}
}
}
公共祖先
那这个不就是后续遍历非递归的变形题吗
cpp
void FindAncestors(BiTree T, int k) {
BiTNode *p = T;
BiTNode *r = NULL;
LinkStack S;
InitStack(S);
while (p != NULL || !StackEmpty(S)) {
if (p != NULL) {
Push(S, p);
p = p->lchild;
} else {
BiTNode *top;
GetTop(S, top);
if (top->rchild == NULL || top->rchild == r) {
Pop(S);
if (top->data == k) {
while (!StackEmpty(S)) {
BiTNode *ancestor = Pop(S);
printf("%d ", ancestor->data->data);
}
return; // 找到k节点后打印所有祖先并返回
}
r = top;
} else {
p = top->rchild;
}
}
}
}
孩子兄弟表示法
以二叉树的形式表示树或者森林
cpp
typedef struct CSNode{
ElemType data; //数据域
struct CSNode *firstchild, *nextsibling; //第一个孩子和右兄弟指针
}CSNode. *CSTree;
求森林的叶子节点总个数
cpp
int leafNodeNum(CSTree root) {
if (root == NULL) {
return 0;
} else if (root->firstchild == NULL) {
// 如果没有子节点,返回1(当前节点作为叶子节点的一个路径)加上兄弟节点的路径数量
return 1 + fun(root->nextsibling);
} else {
// 如果有子节点,返回左子树和右子树路径数量之和
return fun(root->firstchild) + fun(root->nextsibling);
}
}
求树的高度
cpp
int high(CSTree T) {
if (T == NULL) {
return 0; // 空树的高度为0
} else {
// 递归计算左子树的高度和右子树的高度,并返回最大值加1(当前节点)
return max(fun(T->firstchild) + 1, fun(T->nextsibling));
}
}
线索二叉树
cpp
//线索二叉树结点
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild, *rchild;
int ltag, rtag; // 左、右线索标志
}ThreadNode, *ThreadTree;
构建先序线索二叉树
cpp
ThreadNode *pre=NULL:
void preThreadTree(ThreadTree p) {
if (p == NULL) return;
//====对这段代码移动===
if (p->lchild == NULL) {
p->lchild = pre;
p->ltag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
//====对这段代码移动===
if (p->ltag == 0) {
preThreadTree(p->lchild);
}
if (p->rtag == 0) {
preThreadTree(p->rchild);
}
}
线索二叉树的遍历(中序和先序)
cpp
// 获取线索二叉树的第一个节点
ThreadNode* firstNode(ThreadNode *p) {
while (p->ltag == 0) p = p->lchild;
return p;
}
// 获取线索二叉树的下一个节点
ThreadNode* nextNode(ThreadNode *p) {
if (p->rtag == 1)
return p->rchild;
else
return firstNode(p->rchild);
}
// 遍历线索二叉树并访问每个节点
void visit(ThreadTree t) {
for(ThreadNode *p = firstNode(t);p != NULL;p = nextNode(p)){
printf("%d", p->data);
}
}
cpp
ThreadNode* nextNode(ThreadNode *p) {
if (p->tag == 0)
return p->lchild;
else
return p->rchild;
}
// 遍历线索二叉树并访问每个节点
void visit(ThreadTree t) {
for(ThreadNode *p = t;p != NULL;p = nextNode(p)){
printf("%d", p->data);
}
}
二叉排序树
在二叉排序树中查找值为k的结点并返回
cpp
BiTNode* findk(BiTree t, int k){
if(t==NULL) return NULL;
if(t->data==k) return t;
else if(k>t->data) return findk(t->rchild, k);
else return findk(t->lchild, k);
}
在二叉排序树中插入值为k的新结点,插入成功返回1,插入失败返回0
cpp
int insertk(BiTree &t, int k){
if(t==NULL){
t = (BiTNode*)malloc(sizeof(BiTNode));
t->data = k;
t->lchild = NULL;
t->rchild = NULL;
return 1;
}
if(t->data==k) return 0;
else if(k>t->data) return insertk(t->rchild, k);
else return insertk(t->lchild, k);
}
注:在二叉排序树中不允许出现重复的值
判别二叉树是否为二叉排序树
cpp
int pre=-100000;
int ans=1; //1表示二叉树排序树,0则不是
void BST(BiTree T){
if(T==NULL) return;
BST(T->lchild);
if(pre>T->data){
ans=0;
}
pre=T->data;
BST(T->rchild);
}
指定结点在给定二叉排序树中的层次
cpp
void kNodeLevel(BiTree T,int level,int k){//传入根结点时
level=1
if(T==NULL) return;
if(T->data==k) printf("%d" level);
else if(k>T->data)
kNodeLevel(T->rchild, level+1, k);
else
kNodeLevel(T->Lchild, Level+1, k);
}
图
邻接矩阵
邻接矩阵结构体
cpp
#define MaxVertexNum 100 //项目数目的最大值
typedef char VertexType; //顶点对应的数据类型
typedef int EdgeType; //边对应的数据类型
typedef struct{
VertexType Vex[MaxVertexNum]; //顶点集
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表(也就是权重)
int vexnum,arcnum; //图的当前顶点数和边数
}MGraph;
注:行出度、列入度
采用邻接矩阵表示法创建无向网
cpp
// 创建无向图的邻接矩阵
void CreateMGraph(MGraph &G) {
cout << "请输入顶点数和边数: ";
cin >> G.vexnum >> G.arcnum;
// 初始化顶点
cout << "请输入顶点信息: ";
for (int i = 0; i < G.vexnum; i++) {
cin >> G.Vex[i];
}
// 初始化邻接矩阵
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
G.Edge[i][j] = (i == j) ? 0 : INT_MAX; // 自环为0,其他为无穷大
}
}
// 输入边的信息
cout << "请输入边的信息 (格式: 起点 终点 权重): " << endl;
for (int k = 0; k < G.arcnum; k++) {
char v1, v2;
EdgeType weight;
cin >> v1 >> v2 >> weight;
int i = -1, j = -1;
for (int m = 0; m < G.vexnum; m++) {
if (G.Vex[m] == v1) i = m;
if (G.Vex[m] == v2) j = m;
}
if (i != -1 && j != -1) {
G.Edge[i][j] = weight; // 无向图
G.Edge[j][i] = weight; // 无向图
}
}
}
邻接表
邻接表结构体
cpp
#define MaxVertexNum 100 //图中顶点数目的最大值
typedef struct ArcNode{ //边表结点
int adjvex; //该弧所指向的顶点的位置
struct ArcNode *next; //指向下一条弧的指针
//InfoType info; //网的边权值
}ArcNode;
typedef struct VNode{ //顶点表结点
VertexType data; //顶点信息
ArcNode *first; //指向第一条依附该顶点的弧的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; //邻接表
int vexnum,arcnum; //图的顶点数和弧数
}ALGraph; //ALGraph是以邻接表存储的图类型
采用邻接表表示法创建无向网
cpp
// 创建无向图的邻接表
void CreateALGraph(ALGraph &G) {
cout << "请输入顶点数和边数: ";
cin >> G.vexnum >> G.arcnum;
// 初始化顶点
cout << "请输入顶点信息: ";
for (int i = 0; i < G.vexnum; i++) {
cin >> G.vertices[i].data;
G.vertices[i].first = nullptr; // 初始化边表
}
// 输入边的信息
cout << "请输入边的信息 (格式: 起点 终点): " << endl;
for (int k = 0; k < G.arcnum; k++) {
char v1, v2;
cin >> v1 >> v2;
int i = -1, j = -1;
for (int m = 0; m < G.vexnum; m++) {
if (G.vertices[m].data == v1) i = m;
if (G.vertices[m].data == v2) j = m;
}
if (i != -1 && j != -1) {
// 添加边 v1 -> v2
ArcNode *newArc1 = new ArcNode{j, G.vertices[i].first};
G.vertices[i].first = newArc1;
// 添加边 v2 -> v1
ArcNode *newArc2 = new ArcNode{i, G.vertices[j].first};
G.vertices[j].first = newArc2;
}
}
}
邻接表的遍历(BFS、DFS)
cpp
// 深度优先搜索(DFS)
void DFS(ALGraph &G, int v, bool visited[]) {
visited[v] = true; // 标记为已访问
cout << G.vertices[v].data << " "; // 访问顶点
ArcNode *arc = G.vertices[v].first;
while (arc != nullptr) {
if (!visited[arc->adjvex]) {
DFS(G, arc->adjvex, visited); // 递归访问
}
arc = arc->next;
}
}
// 广度优先搜索(BFS)
void BFS(ALGraph &G, int v) {
bool visited[MaxVertexNum] = {false}; // 访问标记
queue<int> q;
visited[v] = true; // 标记为已访问
cout << G.vertices[v].data << " "; // 访问顶点
q.push(v); // 入队
while (!q.empty()) {
int current = q.front();
q.pop();
ArcNode *arc = G.vertices[current].first;
while (arc != nullptr) {
if (!visited[arc->adjvex]) {
visited[arc->adjvex] = true; // 标记为已访问
cout << G.vertices[arc->adjvex].data << " "; // 访问顶点
q.push(arc->adjvex); // 入队
}
arc = arc->next;
}
}
}
图的遍历
深度优先遍历
cpp
bool visited[MAX_VERTEX_NUM];
void DFSTraverse(MGraph G){
int v;
for(v=0; v<G.vexnum; ++v){
visited[v] = FALSE; //初始化已访问标记数据
}
for(v=0; v<G.vexnum; ++v){ //从v=0开始遍历
if(!visited[v]){
DFS(G, v);
}
}
}
广度优先遍历
cpp
//BFS伪代码
bool visited[Max_vertex_num]; //访问标记数组
void BFSTraverse(Graph G) {
for (int i = 0; i < G.vexnum; ++i)
visited[i] = false; //访问数组初始化
InitQueue(Q); //初始化辅助队列Q
for (int i = 0; i < G.vexnum; ++i){
if(!visited[i])
BFS(G, i); //对每个连通分量调用一次BFS
}
}