文章目录
1.
KMP
Code
import java.util.Scanner;
class Solution {
public int strStr(String haystack, String needle,int startIndex) {
int i,k;
int m=needle.length(); int n = haystack.length();
if(n-m<0) return -1;
if(needle == null || m == 0) return 0;
int[] next = new int[m];
next[0] = 0;
for(i=1,k=0;i<m;++i){
k = next[i-1];
while(k>0 && needle.charAt(k)!= needle.charAt(i))
k = next[k-1];
if(needle.charAt(k)==needle.charAt(i)) next[i]=k+1;
else next[i]=0;
}
for(int p=startIndex,q=0;p<n;++p){
while(q>0 && needle.charAt(q)!= haystack.charAt(p))
q = next[q-1];
if(needle.charAt(q)==haystack.charAt(p))
q++;
if(q==m) return p-m+1;
}
return -1;
}
}
public class OriginalKMP{
public static void main(String[] args) {
String pat,txt;
Scanner in = new Scanner(System.in);
txt = in.nextLine();
pat = in.nextLine();
Solution s = new Solution();
int p= s.strStr(txt, pat, 0);
System.out.printf("%-3d ",p);
while(p>0 && (p+pat.length()+pat.length())<txt.length()){
//如果是空的0或者失配的-1,所以p>0作为不继续查询的终止条件
p=s.strStr(txt,pat,p+pat.length());
if(p!=-1){//因为进入循环已经说明没有失配,现在只是检测后面还有没有字串
System.out.printf("%-3d ",p);
}
}
System.out.print("\n");
in.close();
}
}
##Result
- 输入空的字符串匹配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ZXSLtOg-1580202749734)(./1571124215716.png)] - 输入匹配串大于待匹配串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xCXBVVo-1580202749737)(./1571124243816.png)] - 正常输入,串中有两处子串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPTB8wtx-1580202749738)(./1571124181439.png)] - 正常输入,串中有一处子串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2XcX85f-1580202749739)(./1571124980061.png)] - 正常输入,输入圆周率前十万位作为待匹配串
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYdp0mp4-1580202749743)(./1571124757521.png)]
诚信声明:我承诺诚实作业,没有抄袭他人
Code
* @Descripttion:
* @version:
* @Author: Yihui
* @Date: 2019-10-04 20:13:47
* @LastEditors: Yihui
* @LastEditTime: 2019-10-05 09:21:16
*/
//基本思路:让前半部分与从栈出来的后半部分进行比较,如果符合模式要求,则两者应该相等
#include<string>
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define maxSize 100
typedef struct{
char data[maxSize];
int top;
}SqStack;
void iniStack(SqStack &st){
st.top=-1;
}
int pop(SqStack &st,string str,int count){
if(st.top==maxSize-1) return 0;
for(int i=0;i<count;++i){
st.data[++st.top]=str[i+count+1];
}
return 1;
}
char push(SqStack &st){
char x;
if(st.top==-1) return 0;
x=st.data[st.top--];
return x;
}
int main(){
string str;
cout<<"Please enter the sequence:\n";
getline(cin,str,'#');int count;
//求出序列a的长度
for(int i=0;i<maxSize;++i){
if(str[i]=='&') break;
count++;
}
SqStack st;iniStack(st); pop(st,str,count);
int flag=1;
for(int i=0;i<count;++i){
if(str[i]!=push(st)){
flag=0;break;
}
}
//str仍然以'\0'结尾,应该是输入后自动加上去的
if(flag && str[count*2+1]=='\0'){
printf("It is a pattern sequence.");
}else cout<<"It is not a pattern sequence."<<endl;
return 0;
}
计数叶子结点数
/*
* @Descripttion:
* @version: 1.0
* @Author: Yihui
* @Date: 2019-10-20 16:04:12
* @LastEditors: Yihui
* @LastEditTime: 2019-10-20 19:25:35
*/
#include<cstdio>
#include<iostream>
using namespace std;
typedef int ELEMTYPE;
//为了方便用二叉链表表示树,设计出一个含有数据域和两个指针域的树结点
typedef struct Btnode{
ELEMTYPE data;
struct Btnode *lchild;
struct Btnode *rchild;
}BiTreeNode;
//前序创建二叉树
BiTreeNode *createBiTree(){
BiTreeNode *T;
ELEMTYPE data;
scanf("%d",&data);
if(data==-1){
T = NULL;
}else{
T = new BiTreeNode;
T->data = data;
cout<< "请输入"<<data<<"的左结点: ";
T->lchild=createBiTree();
cout<< "请输入"<<data<<"的右结点: ";
T->rchild=createBiTree();
}
return T;
}
//遍历结点,度为0的为叶子结点
void leafCount(BiTreeNode *T,int &count) {
//考虑是空树的情况,T!=NULL
if(T!=NULL){
if(T->lchild==NULL && T->rchild==NULL){
count++;
}else{//当左孩子与右孩子都不存在的时候,即为NULL的时候,不可以继续往下递归找左孩子右孩子了!
leafCount(T->lchild,count);
leafCount(T->rchild,count);
}
}
}
int main(){
cout<< "请输入根结点的值: ";
BiTreeNode *root = createBiTree();
int count = 0;
leafCount(root,count);
printf("叶子结点数为:%d\n",count);
return 0;
}
镜面影射算法
//设计一个镜面影射算法,将一棵二叉树的每个结点的左右子结点交换位置
//需要通过前序+中序遍历或者中序+后序遍历输出数组唯一确定一棵树,以表明最后真的是把这棵树的所有子树互换了
//递归
#include <cstdio>
#include <iostream>
using namespace std;
typedef char ELEMENT;
typedef struct BiTNode{
ELEMENT data;
struct BiTNode *rchild,*lchild;
}BiTree;
//前序遍历建树
BiTree *CreateBiTree(){
ELEMENT data;
cin>>data;
BiTree *root;
if(root == NULL){
cout<<"内存分配失败";
return NULL;
}
if(data=='@'){
return NULL;
}else{
root = new BiTree;
root->data = data;
cout<<"请输入"<<root->data<<"的左孩子: ";
root->lchild=CreateBiTree();
cout<<"请输入"<<root->data<<"的右孩子: ";
root->rchild=CreateBiTree();
}
return root;
}
void preOrderTranverse(BiTree *root){
if(root==NULL) return;//就是返回,没有返回值!!递归终止条件
printf("%c",root->data);
preOrderTranverse(root->lchild);
preOrderTranverse(root->rchild);
}
void inOrderTranverse(BiTree *root){
if(root==NULL) return;
inOrderTranverse(root->lchild);
printf("%c",root->data);
inOrderTranverse(root->rchild);
}
//递归不停地交换子树的左右子树,一棵二叉树的每个结点的左右子结点交换的结果--->镜像,有些好玩!
BiTree *reverseTree(BiTree *root){
if(root==NULL) return NULL;
//如果先在前面进入递归?这打紧吗?-->尝试了,不打紧
BiTree *temptree = root->lchild;
root->lchild = root->rchild;
root->rchild = temptree;
root->lchild = reverseTree(root->lchild);
root->rchild = reverseTree(root->rchild);
return root;
}
int main(){
cout<< "请输入根结点: ";
BiTree *root;
root=CreateBiTree();
preOrderTranverse(root);
cout<<"\n";
inOrderTranverse(root);
cout<<"\n";
reverseTree(root);
preOrderTranverse(root);
cout<<"\n";
inOrderTranverse(root);
return 0;
}
BST删除关键字算法
#include<cstdio>
#include<iostream>
using namespace std;
typedef int ELEMENT;
typedef struct BiTNodes{
ELEMENT key;//这里将data换成key,代表关键字
struct BiTNodes *rchild;
struct BiTNodes *lchild;
}*BiTree,BiTNode;
void preOrder(BiTree root){
if(root==NULL) return;
cout<<root->key<<" ";
preOrder(root->lchild);
preOrder(root->rchild);
}
int BSTinsert(BiTree &root, ELEMENT key){
if(root==NULL){
root= new BiTNode;
root->lchild=root->rchild=NULL;
root->key=key;
return 1;
}else{
if(key==root->key)
return 0;
else if(key<root->key)
return BSTinsert(root->lchild,key);
else
return BSTinsert(root->rchild,key);
}
}
void createBSTree(BiTree &root,ELEMENT n){//n为结点个数
root=NULL;//将树清空
ELEMENT key;
for(int i=0;i<n;++i){
cin >>key;
BSTinsert(root,key);
}
}
//找出值最大的结点
BiTree findMax(BiTree root){
while(root->rchild)
root = root->rchild;
return root;
}
//找出值最小的结点
BiTree findMin(BiTree root){
while(root->lchild)
root = root->lchild;
return root;
}
void deleteNode(BiTree &root,ELEMENT x){
if(root==NULL) return;
if(root->key==x){
if(root->lchild==NULL && root->rchild==NULL)
root=NULL;
else if(root->lchild!=NULL){
BiTree pre = findMax(root->lchild);//找root的前驱,左子树最大的也就是必然比root大一个的那一个值
root->key=pre->key;
//用前驱覆盖root
deleteNode(root->lchild,pre->key);
//在左子树中删除结点pre
}else{
BiTree post =findMin(root->rchild);
//找root的后继,右子树最小的也必然就是比root小一个的那一个值
root->key=post->key;
//用后继覆盖root
deleteNode(root->rchild,post->key);
//在右子树中删除结点post
}
}else if(root->key>x){
deleteNode(root->lchild,x);
//大于x说明x应当在root的左子树
}else deleteNode(root->rchild,x);
}
int main(){
BiTree root;
int n;
cout<<"请输入结点的个数:";
cin >> n;
cout<<"请输入关键字:";
createBSTree(root,n);
// preOrder(root);
cout<<"请输入要删除的结点值:";
ELEMENT target;
cin>>target;
deleteNode(root,target);
preOrder(root);
return 0;
}
最小堆建立哈夫曼树并编码输出
思路
- 创建最小堆
- 建立新的Huffuman结点并放入最小堆的数组分量中
- 将字符与权值分别赋给数组分量中的字符分量与权值分量
- 构造哈夫曼树:
- 将认为存储完全二叉树的数组调整为最小堆
- 从最小堆中每次选出最小的二个结点生成新的Huffauman结点的左右子结点
- N个叶子结点合并生成的N-1个权值为最小的二个子结点之和
- 重复上述过程,最后将根结点也取出
- 沿着路径对哈夫曼树编码并输出,同时传入指针变量进行求和作为总编码
##Code
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
// 在这里哈夫曼树默认用完全二叉树的最小堆来实现,用数组data[]存储!!!
typedef struct TreeNode{
char ch[2];//因为每个叶子结点的权值可能一样,所以要用实际要编码的字符进行区分
int weight; //权值
struct TreeNode* left;
struct TreeNode* right;
}HuffmanNode,*HuffmanTree;
#define MinData -1 //随着堆元素的具体值而改变
typedef struct HeapStruct{
int size;//堆的当前元素的个数
int capacity;//堆的最大容量,这个分量用来指示数组的大小
HuffmanTree *data; //存储堆元素的数组 从下标1开始
//这个data居然是指向 结构体指针类型的 指针.
// 因为要用数组来存储完全二叉树,所以堆结构体中一定包含有一个数组的指针,即data
}MinHeapNode,*MinHeap;
HuffmanTree NewHuffmanNode();
MinHeap CreateMinHeap(int MaxSize);
bool Insert(MinHeap H,HuffmanTree item);
HuffmanTree DeleteMin(MinHeap H);
MinHeap BuildMinHeap(MinHeap H);
HuffmanTree Huffman(MinHeap H);
void preOrder(HuffmanTree BST);
void inOrder(HuffmanTree BST);
void HuffmanCode(HuffmanTree BST,int depth,int *cnt);
int main(){
int i,N;
MinHeap h;
HuffmanTree T,BT = NULL;
printf("请输入叶子结点的个数即字符的种数:\n");
scanf("%d",&N);
//叶子结点也即要编码的字符
h = CreateMinHeap(2*N); //创建最小堆
//N个叶子结点的最终形成的哈夫曼树最多有2N-1个树结点
for(i=1;i<=N;i++){
T = NewHuffmanNode();
h->data[++(h->size)] = T;//!!!
}
printf("请连续输入这%d个叶子结点各自代表的字符:\n",N);
getchar();//吸收上面的换行符,因为scanf()是从输入流中取元素-->似乎没用?
// 如果比如是c1,c2,c3,c4,c5,c6...那每个ch分量都得是string啊,这样就需要一个更大的容器封装这些string,然后再一个个赋给ch,可以做到吗?-->不必要,如下scanf可以
// //给最小堆中元素的ch分量赋值
for(i=1;i<=h->size;i++){//size是当前堆元素个数,也就是N!
scanf("%s",h->data[i]->ch);
// h->data[i]->ch = string[i-1];
//TODO 为啥是i-1?因为结点是从1开始正式存储值的
}
printf("请输入%d个叶子结点对应的权值:\n",N);
//最小堆元素赋值
for(i=1; i<=N; i++){//不理解之全都是坑,i从1开始
scanf("%d",&(h->data[i]->weight));
}
BT = Huffman(h);//构造哈夫曼树
printf("先序遍历哈夫曼树的权值:\n");
preOrder(BT); printf("\n");
printf("中序遍历此哈夫曼树的权值:\n");
inOrder(BT); printf("\n");
int cnt=0;
HuffmanCode(BT,0,&cnt);//cnt传递进去自己地址才能递归调用时自身不被重置
printf("总编码数=Σ(路径长度*出现频率(对应权值)):%d\n",cnt);
return 0;
}
//哈夫曼树构造算法
HuffmanTree Huffman(MinHeap H){
//假设H->size个权值已经存在H->data[]->weight里
int i,num;
HuffmanTree T;
BuildMinHeap(H);//将H->data[]按权值调整为最小堆
//此处必须将H->size的值交给num,因为后面做DeleteMin()和Insert()函数会改变H->size的值,一个减少,一个增加
num = H->size;//此时,H->size还是N,因为之前拷贝进H的就是N个HuffmanTree结点
// cout<<"<"<<H->size<<">";
for (i = 1; i < num;i++){//做H->size-1次合并!!!!
T = NewHuffmanNode();//建立一个新的根结点
T->left = DeleteMin(H);//从最小堆中取出权值最小的元素作为新的左子结点
// 注意DeleteMin()返回值本身也就是HuffmanTree类型!!
T ->right = DeleteMin(H);//从最小堆中取出权值最小的元素作为新的右子结点
//哈夫曼树之所以能被称作树,之所以能递归遍历正是因为上面这两句啊!
T ->weight = T ->left->weight + T ->right->weight;//计算新权值
Insert(H,T);//将新T插入到最小堆
}
T = DeleteMin(H);// 原本N个结点,新生成N-1个结点插入,做了N-1次合并取出2(N-1)个结点,最后剩下1个生成的HuffmanTree就是根结点
// cout<<"<"<<H->size<<">";
return T;
}
//先序遍历
void preOrder(HuffmanTree BST){
if(BST == NULL) return;
cout<< BST->weight<<" ";
preOrder(BST->left);
preOrder(BST->right);
}
//中序遍历
void inOrder(HuffmanTree BST){
if(BST == NULL) return;
inOrder(BST->left);
cout<< BST->weight<<" ";
inOrder(BST->right);
}
//建立新的根结点
HuffmanTree NewHuffmanNode(){
HuffmanTree BST = new HuffmanNode;//定义HuffmanNode的用处似乎就在这
BST->weight = 0;
BST->left = BST->right = NULL;
return BST;
}
//创建容量为MaxSize的最小堆
MinHeap CreateMinHeap(int MaxSize){
MinHeap H = new MinHeapNode;
H -> data = (HuffmanTree *)malloc((MaxSize+1) * sizeof(HuffmanTree));
//MaxSize +1,因为堆的正式元素的存放是从下标为1的元素存放的
H->size = 0;
H ->capacity =MaxSize;
HuffmanTree T = NewHuffmanNode();
T->weight = MinData;
//定义"哨兵"为小于堆中所有可能元素的值,便于以后更快操作
H->data[0] = T;//没有用到的首地址元素留着做哨兵
return H;
}
//这是借鉴了队列满与空的判断方法?
bool isFull(MinHeap H){
return (H->size == H->capacity);
}
bool isEmpty(MinHeap H){
return (H->size == 0);
}
//插入算法--将新增结点插入到 从其父结点到根结点的有序序列中
bool Insert(MinHeap H,HuffmanTree item){
//将结构体指针元素item插入到最小堆H中,在这里H->data[0]已被定义为哨兵
int i;
if(isFull(H)){
cout<<"最小堆已满\n";
return false;
}
i = ++H->size; //如果堆不满,就把元素放到堆中最后一个元素的位置,也即size+1的位置,故而++
//最小堆中哨兵是最小的权值(所以这里赋了-1)且位于0地址的位置,使得孩子与父结点比较不会越过哨兵的界,也就是不会越过堆的界;无哨兵,则必须增加判决条件i>1,防止插入权值小于所有元素,而使得0结点乃至越界的都会往下移的情况<--增加哨兵其实是少了一次判断,提高运行效率
//i是当前位置,i/2正好是父结点的位置,把这两个位置比较调换(如果父结点大于要插入的结点权值),保持最小堆的有序性
while(H->data[i/2]->weight > item->weight){
H->data[i] = H->data[i/2];//让不满足结点小于下面值的大结点下来
i/=2;//不断地定位到父结点
}
H->data[i] = item;//将item插入
return true;
}
//从最小堆H中取出权值为最小的元素,并删除一个结点
HuffmanTree DeleteMin(MinHeap H){
int parent,child;
HuffmanTree MinItem,temp = NULL;
if(isEmpty(H)){
cout<<"最小堆为空\n";
return NULL;
}
MinItem = H->data[1];//权值最小,也就是要优先取出,即删除的元素,先保存着最后return返回
//用最小堆中的最后一个元素从根结点开始向上过滤下层结点
//TODO 过滤是什么意思?-->为了找出最小的元素
temp = H->data[H->size--];//最小堆中最后一个元素,先假设把最后一个元素拿上来放到替换删掉的最上面的最小值结点,接下来通过循环来找放上来的这个值的合适位置,并把合适位置的元素交换到最上面的这个位置-->所以下面循环来找位置
//--H->size可以吗?不可以,因为不仅要把最后一个结点赋给temp,还要把原本的标识往回退一
for (parent =1;parent*2 <= H->size;parent=child){//parent=1即放到最上面的第一位,正式元素的存储是从下标为1开始的;child如果大于H->size,child就在最后一个元素之外了
child = parent*2;//默认将child放在其左孩子的地方,假设左孩子大(接着下面然后比较)
if((child != H->size) && (H->data[child]->weight > H->data[child+1]->weight))//child如果等于H->size,即左孩子就是指向最后一个元素,自然也就没有必要后面比较了.这句话也就是为了提高效率
child++;//上面条件满足的话,child指向右孩子,即child指向左右孩子中较小者
if(temp->weight < H->data[child]->weight) break; // 如果上面位置的temp的值小于左右孩子中较小的值,这个比较的成功情况也最多只会执行一次,已找到parent的合适的位置
else
H->data[parent] = H->data[child];// 左右孩子中较小的那个和temp假设所在的位置---parent进行比较,如果依然大于parent,说明parent应该下到child的位置,同时child上到parent的位置
// 比较的时候比较权值,调换的时候调换haffuman
}
H->data[parent] = temp;//temp存放到此处
return MinItem;//取出权值最小的结点
}
//调整H->data[]中的元素,使其满足堆的有序性
MinHeap BuildMinHeap(MinHeap H){
//这里假设所有的H->size个元素已经存在H->data[]中
int i,parent,child;
HuffmanTree temp;
for(i=H->size/2;i>0;i--){//从最后一个父结点开始,一直调整直到根结点
temp = H->data[i];
for(parent =i;parent*2 <= H->size;parent=child){
// 向下过滤
child = parent*2;
if((child != H->size) && (H->data[child]->weight > H->data[child+1]->weight))//有右孩子,并且左孩子权值大于右孩子
child++;//child的数值移动-->child指向左右孩子最小值
if(temp->weight < H->data[child]->weight ) break;
else H->data[parent] = H->data[child];
}//结束内部for()循环对以H->data[i]为根的子树的调整
H ->data[parent] = temp;//temp(原H->data[i])存放到此处
}
return H;
}
//一遍沿着路径编码,然后遇到叶子结点就把之前的暂存的码都输出来,下一次重新开始
void HuffmanCode(HuffmanTree BT,int depth,int *p_cnt){
// depth 为目前编码到哈夫曼树的深度(层次)
static int code[10];
if(BT!=NULL){
if((BT->left == NULL) && (BT->right == NULL)){
cout<<"字符"<<BT->ch<<"对应权值为"<<BT->weight<<"的哈夫曼编码为:";
int i;
//depth是路径长度,为高度-1,等于深度,它原本是跟随着寻找叶子结点的递归的调用然后+1的!
for(i=0;i<depth;i++){
cout<<code[i];
}
cout<<"\n";
*p_cnt =(*p_cnt) + (BT->weight) * depth;
// cout<< "<"<<*p_cnt<<"> ";
}else{
code[depth] = 0; //往左子树方向编码为0
HuffmanCode(BT->left,depth+1,p_cnt);
// cout<< "<"<<*p_cnt<<"> ";
code[depth] = 1; //往右子树方向编码为1
HuffmanCode(BT->right,depth+1,p_cnt);
}
}
}
判断两树是否相同(考虑孩子顺序时)
思路
为便于建树排序,考虑到运行效率,采用树的左孩子右兄弟.建树例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA4nBNG1-1580202749762)(./1573470593848.png)]
- 给结点从上到下层序依次编号,习惯将根结点从0开始
- 依次读入数据与做孩子右兄弟的编号,如果输入-1则代表这个孩子结点不存在
- 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
- 先根遍历比较两棵树
具体细节请见代码
Code
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef struct TreeNode{
char data;
int childNo,siblingNo;
TreeNode *lchild;
TreeNode *rsibling;
}*Tree,TreeNode;
void CreateTree(int root,vector<Tree> tree);
bool preCompare(Tree root1,Tree root2);
void preOrder(Tree T);
int main(){
vector<Tree> tree1;
vector<Tree> tree2;
int n1,n2;
cout << "请输入第一棵树与第二棵树的结点数目:"<<endl;
// scanf多用于格式化输入的时候
cin>>n1>>n2;//第一棵树与第二棵树的结点数目
if(n1 != n2){
cout << "两树不同\n"<<endl;
return 0;
}
int root1,root2;
cout << "请输入第一棵树与第二棵树的根结点编号:"<<endl;
cin>>root1>>root2;//输入第一棵树与第二棵树的根节点
// 输入第一棵树每一行的data,左孩子编号与右兄弟编号
cout << "请输入第一棵树每一行的data,左孩子编号与右兄弟编号:"<<endl;
getchar();
for(int i=0;i<n1;i++){
Tree temp = new TreeNode;
cin>>temp->data>>temp->childNo>>temp->siblingNo;
temp->lchild = temp->rsibling = NULL;
tree1.push_back(temp);
}
cout << "请输入第二棵树每一行的data,左孩子编号与右兄弟编号:"<<endl;
// 输入第二棵树每一行的data,左孩子编号与右兄弟编号
for(int i=0;i<n2;i++){
Tree temp = new TreeNode;
cin>>temp->data>>temp->childNo>>temp->siblingNo;
temp->lchild = temp->rsibling = NULL;
tree2.push_back(temp);
// cout<<"<"<<tem->data<<"> "<<endl;
}
CreateTree(root1,tree1);
CreateTree(root2,tree2);
// preOrder(tree1[root1]);
// preOrder(tree2[root2]);
if(preCompare(tree1[root1],tree2[root2])) cout << "两树相同\n"<<endl;
else cout << "两树不同\n";
return 0;
}
//建立的时候能结束吗?输入到什么时候这个是终止的呢?
void CreateTree(int root,vector<Tree> tree){
if(tree[root]->childNo != -1){
tree[root]->lchild = tree[tree[root]->childNo];
CreateTree(tree[root]->childNo,tree);
}
if(tree[root]->siblingNo != -1){
tree[root]->rsibling = tree[tree[root]->siblingNo];
CreateTree(tree[root]->siblingNo,tree);
}
}
bool preCompare(Tree root1,Tree root2){
if(root1 !=NULL && root2 !=NULL ){
if(root1->data != root2->data) return false;
return preCompare(root1->lchild,root2->lchild) && preCompare(root1->rsibling,root2->rsibling);
}else if(( root1 == NULL && root2 != NULL ) || (root2 == NULL && root1 != NULL)) return false;
return true;
}
void preOrder(Tree T){
if(!T) return;
printf("%c\n", T->data);
preOrder(T->lchild);
preOrder(T->rsibling);
}
效率分析
建树分别需要O(n1)和O(n2),前序递归遍历两树需要O(n),所以算法时间复杂度为O(n)
判断两树是否相同(忽略孩子顺序时)
思路
为便于建树排序,采用树的子结点存储法.因为题目没有给输入,因此简单一边建树,例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uQUxYDPn-1580202749768)(./1573463195203.png)]
- 给结点从上到下层序依次编号,习惯将根结点从0开始
- 依次读入数据与孩子结点的数目以及各个孩子依次的编号,通过孩子结点数目的递减控制输入,类似于数组依次创建结点编号
- 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
- 层序遍历比较两棵树
具体细节请见代码
Code
#include <cstdio>
#include <iostream>
#include <vector>
#include<queue>
#include<algorithm>
using namespace std;
#define maxSize 100000
// 这里不可以用typedef
//树的子结点表示法
struct TreeNode{
char data;
vector<int> childNo;
}Tree1[maxSize],Tree2[maxSize];
bool cmp1(int a,int b);
bool cmp2(int a,int b);
bool levelOrder(int root1,int root2);
int main (){
int n1,n2;
cout << "请输入第一棵树与第二棵树的结点数目:"<<endl;
cin >>n1>>n2;
if(n1 != n2){
cout<<"两树不同\n";
return 0;
}
int root1,root2;
cout << "请输入第一棵树与第二棵树的根结点编号:"<<endl;
cin>>root1>>root2;//输入第一棵树与第二棵树的根节点
cout << "请输入第一棵树每一个结点的data,孩子的数目与孩子的编号(但是必须输够10行):"<<endl;
for(int i=0;i<n1;i++){
int childNum,childNo;
cin >>Tree1[i].data>>childNum;
while(childNum--){
cin>>childNo;
Tree1[i].childNo.push_back(childNo);
}
}
cout << "请输入第二棵树每一个结点的data,孩子的数目与孩子的编号(但是必须输够10行):"<<endl;
for(int i=0;i<n2;i++){
int childNum,childNo;
cin>>Tree2[i].data>>childNum;
while(childNum--){
cin>>childNo;
Tree2[i].childNo.push_back(childNo);
}
}
if(levelOrder(root1,root2))
cout<<"两树相同\n";
else
cout<<"两树不同\n";
return 0;
}
bool cmp1(int a,int b){
return Tree1[a].data > Tree1[b].data;
}
bool cmp2(int a,int b){
return Tree2[a].data > Tree2[b].data;
}
bool levelOrder(int root1,int root2){
queue<int> q1,q2;
q1.push(root1);
q2.push(root2);
while(!q1.empty() && !q2.empty()){
int front1 =q1.front();
int front2 =q2.front();
q1.pop();
q2.pop();
if(Tree1[front1].childNo.size() != Tree2[front2].childNo.size()) return false;
// 对结点data按Unicode码大小排序,消除孩子顺序的影响
sort(Tree1[front1].childNo.begin(), Tree1[front1].childNo.end(),cmp1);
sort(Tree2[front2].childNo.begin(), Tree2[front2].childNo.end(),cmp2);
// 判断孩子是否相同
for(int i = 0; i <Tree1[front1].childNo.size();i++){
if(Tree1[Tree1[front1].childNo[i]].data != Tree2[Tree2[front2].childNo[i]].data)
return false;
}
}
return true;
}
效率分析
层序遍历需要O(n),为了消除结点顺序需要给结点排序,快为O(log2n),所以算法时间复杂度为 O ( n ∗ l o g 2 n ) O(n*log2n) O(n∗log2n)
计算树的结点数目
思路
为便于建树排序,采用树的子结点存储法.因为题目没有给输入,因此简单一边建树,例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqVG3BjN-1580202749774)(./1573463195203.png)]
- 给结点从上到下层序依次编号,习惯将根结点从0开始
- 依次读入数据与孩子结点的数目以及各个孩子依次的编号,通过孩子结点数目的递减控制输入,类似于数组依次创建结点编号
- 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
- 层序遍历计算树的结点值(虽然一开始就已经输入了结点的数目…)
具体细节请见代码.注意编号只是习惯如此,不对实际顺序进行约束.
Code
#include <cstdio>
#include <iostream>
#include <vector>
#include<queue>
#include<algorithm>
using namespace std;
#define maxSize 100000
struct TreeNode{
char data;
vector<int> childNo;
}Tree[maxSize];
void CreateTree(int n);
int levelOrder(int root);
int main(){
int n;
cout << "请输入树的结点数目:"<<endl;
cin >> n;
if(n==0){
cout<<"结点数目: "<<n;
return 0;
}
int root;
cout << "根的编号:"<<endl;
cin >> root;
CreateTree(n);
cout<<"结点数目: "<<levelOrder(root);
return 0 ;
}
void CreateTree(int n){
cout << "请输入树每一个结点的data,孩子的数目与孩子的编号(但是必须输够n行):"<<endl;
for(int i = 0; i < n;i++){
int childNum,childNo;
cin>>Tree[i].data>>childNum;
while(childNum--){
cin>>childNo;
Tree[i].childNo.push_back(childNo);
}
}
}
int levelOrder(int root){
queue<int> q;
q.push(root);
int count = 0;
while(!q.empty()){
int front = q.front();
int childNum = Tree[q.front()].childNo.size();
q.pop();
int childNo=0;
count++;//结点计数
while(childNum--){
q.push(Tree[front].childNo[childNo++]);
}
}
return count;
}
将树转化为对应的二叉树
思路
为便于建树排序,考虑到运行效率,采用树的左孩子右兄弟.建树例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6CqmKKb-1580202749783)(./1573470593848.png)]
- 给结点从上到下层序依次编号,习惯将根结点从0开始
- 依次读入数据与做孩子右兄弟的编号,如果输入-1则代表这个孩子结点不存在
- 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
- 先序与中序输出唯一确定这棵二叉树
具体细节请见代码
Code
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef struct TreeNode{
char data;
int childNo,siblingNo;
TreeNode *lchild;
TreeNode *rsibling;
}*Tree,TreeNode;
void preOrder(Tree root);
void inOrder(Tree root);
void CreateTree(int root,vector<Tree> tree);
int main() {
int n;
cout << "请输入树的结点数目:"<<endl;
cin>>n;
int root;
cout << "根结点的编号:"<<endl;
cin>> root;
vector<Tree> tree;
cout << "请输入每一个结点的字符以及它的左孩子右兄弟的编号"<<endl;
for(int i=0;i<n;i++){
Tree temp = new TreeNode;
cin>>temp->data>> temp->childNo >> temp->siblingNo;
temp->lchild = temp->rsibling = NULL;
tree.push_back(temp);
}
CreateTree(root,tree);
cout << "前序遍历:"<<endl;
preOrder(tree[root]);
cout <<"\n";
cout << "中序遍历:"<<endl;
inOrder(tree[root]);
return 0 ;
}
void preOrder(Tree root){
if(!root) return;
cout <<root->data<<" ";
preOrder(root->lchild);
preOrder(root->rsibling);
}
void inOrder(Tree root){
if(!root) return;
inOrder(root->lchild);
cout <<root->data<<" ";
inOrder(root->rsibling);
}
void CreateTree(int root,vector<Tree> tree){
if(tree[root]->childNo != -1){
tree[root]->lchild = tree[tree[root]->childNo];
CreateTree(tree[root]->childNo,tree);
}
if(tree[root]->siblingNo != -1){
tree[root]->rsibling = tree[tree[root]->siblingNo];
CreateTree(tree[root]->siblingNo,tree);
}
}
对英语单词基数排序
Thinking
请见注释.
Code
#include <stdio.h>
#include <string.h>
#include<iostream>
#include<string>
using namespace std;
#define MaxLen 9 //单词的最大长度
#define Radix 27 //基数rd为27,分别对应' ','a',…'z'
typedef char String[MaxLen+1]; //定义String为字符数组类型
typedef struct node
{
String word;
struct node *next;
} LinkNode;
void DispWord(String R[],int n) //输出单词
{
int i;
printf(" ");
for (i=0; i<n; i++)
printf("[%s] ",R[i]);
printf("\n");
}
void PreProcess(String R[],int n)
//对单词进行预处理,用空格填充尾部至MaxLen长
{
int i,j;
for (i=0; i<n; i++)
{
if (strlen(R[i])<MaxLen)
{
for (j=strlen(R[i]); j<MaxLen; j++)
R[i][j]=' ';
R[i][j]='\0';
}
}
}
void EndProcess(String R[],int n)
//恢复处理,删除预处理时填充的尾部空格
{
int i,j;
for (i=0; i<n; i++)
{
for (j=MaxLen-1; R[i][j]==' '; j--);
R[i][j+1]='\0';
}
}
void Distribute(String R[],LinkNode *head[],LinkNode *tail[],int j,int n)
//按关键字的第j个分量进行分配,进入此过程时各队列一定为空
{
int i,k;
LinkNode *p;
for (i=0; i<n; i++) //依次扫描R[i],将其入队
{
if (R[i][j]==' ') //空格时放入0号队列中,'a'时放入1号队列中,…
k=0;
else
k=R[i][j]-'a'+1;
p=(LinkNode *)malloc(sizeof(LinkNode)); //创建新结点
strcpy(p->word,R[i]);
p->next=NULL;
if (head[k]==NULL)
{
head[k]=p;
tail[k]=p;
}
else
{
tail[k]->next=p;
tail[k]=p;
}
}
}
void Collect(String R[],LinkNode *head[])
//依次将各非空队列中的记录收集起来
{
int k=0,i;
LinkNode *p;
for (i=0; i<Radix; i++)
for (p=head[i]; p!=NULL; p=p->next)
strcpy(R[k++],p->word);
}
void RadixSort(String R[],int n) //对R[0..n-1]进行基数排序
{
LinkNode *head[Radix],*tail[Radix]; //定义Radix个队列
int i,j;
for (i=MaxLen-1; i>=0; i--) //从低位到高位做d趟箱排序
{
for (j=0; j<Radix; j++)
head[j]=tail[j]=NULL; //队列置空
Distribute(R,head,tail,i,n); //第i趟分配
Collect(R,head); //第i趟收集
}
}
int main()
{
int n;
cout <<"请输入要排序的字符串行数:";cin>>n;
String R[n];
getchar();
for(int i=0; i<n; i++)
cin>>R[i];
// for(int i=0; i<n; i++)
// cout<<R[i]<<endl;
printf("排序前:\n");
DispWord(R,n);
PreProcess(R,n);
printf("预处理后:\n");
DispWord(R,n);
RadixSort(R,n);
printf("排序结果:\n");
DispWord(R,n);
EndProcess(R,n);
printf("最终结果:\n");
DispWord(R,n);
printf("\n");
return 0;
}
顺序检索Vs二分检索
Code
#include <cstdio>
#include <iostream>
#include <math.h>
using namespace std;
int Seqsearch(int a[],int length,int key,int *s);
int Bsearch(int a[],int low,int high,int key,int *s);
void Quicksort(int a[],int low,int high);
#define Num 10000
int main(){
int a[Num];
for(int i=0;i<Num;i++)
a[i] = ceil(1+rand()%20000);
Quicksort(a,0,Num);
double sum_Seq = 0.0,sum_B=0.0;
int successNum_Seq=0;
int successNum_B=0;
int temp;
for(int i=0;i<Num;i++){
temp = ceil(1+rand()%20000);// 产生的随机数可能太随机以至于全都不相等吧....
sum_Seq += Seqsearch(a,Num,temp,&successNum_Seq);
sum_B += Bsearch(a,0,Num,temp,&successNum_B);
}
cout <<"检索总次数:"<<Num<<endl;
cout<<"顺序检索成功的次数:"<<successNum_Seq<<endl;
cout<<"二分检索成功的次数:"<<successNum_B<<endl;
cout<<"顺序检索成功的百分比:"<<successNum_Seq*100.0/Num<<"%"<<endl;
cout<<"二分检索成功的百分比:"<<successNum_B*100.0/Num<<"%"<<endl;
cout<<"顺序检索的平均比较次数:"<<sum_Seq/Num<<endl;
cout<<"二分检索的平均比较次数:"<<sum_B/Num<<endl;
return 0;
}
int Seqsearch(int a[],int length,int key,int *s){
int count=0;
for(int i=0;i<length;i++){
count++;
if(a[i] == key){
(*s)++;
return count;
}
}
return length;
}
int Bsearch(int a[],int low,int high,int key,int *s){
int mid;int count=0;
// 这下面也相当于递归了?!!
while(low <= high){
count++;
mid = (low+high)/2;//自动向下取整
if(a[mid]== key){
(*s)++;
return count;
}
else if(a[mid]<key) low=mid+1;//k大于它在它右边找
else high=mid-1;
}
return count;
}
void Quicksort(int a[],int low,int high){
int i=low,j=high;
if(low<high){
int pivot=a[low];
while(i<j){
while(a[j]>=a[low] && i<j) --j;
if(a[j] < a[low] && i<j){
a[i]=a[j];
++i;
}
while(a[i]<a[low] && j>i) ++i;
if(a[i] > a[low] && j>i){
a[j]=a[i];
--j;
}
}
a[i] = pivot;
Quicksort(a,low,i-1);
Quicksort(a,i+1,high);
}
}