数据结构_一些算法


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

  1. 输入空的字符串匹配
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ZXSLtOg-1580202749734)(./1571124215716.png)]
  2. 输入匹配串大于待匹配串
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xCXBVVo-1580202749737)(./1571124243816.png)]
  3. 正常输入,串中有两处子串
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPTB8wtx-1580202749738)(./1571124181439.png)]
  4. 正常输入,串中有一处子串
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2XcX85f-1580202749739)(./1571124980061.png)]
  5. 正常输入,输入圆周率前十万位作为待匹配串
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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;
}

最小堆建立哈夫曼树并编码输出

思路

  1. 创建最小堆
  2. 建立新的Huffuman结点并放入最小堆的数组分量中
  3. 将字符与权值分别赋给数组分量中的字符分量与权值分量
  4. 构造哈夫曼树:
    • 将认为存储完全二叉树的数组调整为最小堆
    • 从最小堆中每次选出最小的二个结点生成新的Huffauman结点的左右子结点
    • N个叶子结点合并生成的N-1个权值为最小的二个子结点之和
    • 重复上述过程,最后将根结点也取出
  5. 沿着路径对哈夫曼树编码并输出,同时传入指针变量进行求和作为总编码
    ##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)]

  1. 给结点从上到下层序依次编号,习惯将根结点从0开始
  2. 依次读入数据与做孩子右兄弟的编号,如果输入-1则代表这个孩子结点不存在
  3. 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
  4. 先根遍历比较两棵树
    具体细节请见代码

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)]

  1. 给结点从上到下层序依次编号,习惯将根结点从0开始
  2. 依次读入数据与孩子结点的数目以及各个孩子依次的编号,通过孩子结点数目的递减控制输入,类似于数组依次创建结点编号
  3. 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
  4. 层序遍历比较两棵树
    具体细节请见代码

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(nlog2n)

计算树的结点数目

思路

为便于建树排序,采用树的子结点存储法.因为题目没有给输入,因此简单一边建树,例子如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqVG3BjN-1580202749774)(./1573463195203.png)]

  1. 给结点从上到下层序依次编号,习惯将根结点从0开始
  2. 依次读入数据与孩子结点的数目以及各个孩子依次的编号,通过孩子结点数目的递减控制输入,类似于数组依次创建结点编号
  3. 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
  4. 层序遍历计算树的结点值(虽然一开始就已经输入了结点的数目…)
    具体细节请见代码.注意编号只是习惯如此,不对实际顺序进行约束.

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)]

  1. 给结点从上到下层序依次编号,习惯将根结点从0开始
  2. 依次读入数据与做孩子右兄弟的编号,如果输入-1则代表这个孩子结点不存在
  3. 数据值依次对应0~n-1(n为结点数目)赋值给对应编号的结点
  4. 先序与中序输出唯一确定这棵二叉树
    具体细节请见代码

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);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值