排序 & 堆 &二叉树

复习总结,代码及基本内容来源:
《算法笔记》
《算法竞赛入门经典》

排序

  • 稳定排序和不稳定排序

在简单形式化一下,如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。
选择排序、快速排序是不稳定排序(STL 方法sort)。冒泡排序、插入排序是稳定排序(STL方法 stable_sort)。

冒泡排序(稳定排序)

void Bubblesort(int a[],int n){
    int i,j,temp;
    for(int i=0;ia[j+1]){
                swap(a[j],a[j+1]);
            }
        }
    }
}

插入排序的思想(稳定)

插入排序的思想 将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中

比如将a[i] , 插入a[0:i-1]中

int A[maxn],n;
void insertSort(){
    for(int i=2;i<=n;i++){
        int temp=A[i],j=i;
        while(j>1 && temp

选择排序(不稳定排序)

其它

归并排序(稳定)O(n logn)

快速排序(不稳定)比较好:O(n logn) 比较不好:O(n^2)

一般一个在最左,一个在最右,最左找小于基准,最右找大于基准,然后交换。

然后左边的找基准,重复进行;然后右边的找基准,重复进行。(可以用递归)

向上调整方法,向下调整方法

完全树,左孩子2i位,右孩子2i+1位

const int maxn=100;//
int heap[maxn],n=10;
//n,一共多少个元素
void downAdjust(int low,int high){//最大堆,
    int i=low,j=i*2;//i为欲调整节点,j为其左孩子
    while(j<=high){//就是没有到最后
        
        if(j+1<=high&&heap[j+1]>heap[j]){//右孩子存在且值大于左孩子 
            j=j+1;
         }
        if(heap[j]>heap[i]){
            swap(heap[j],heap[i]);//交换函数
            i=j;
            j=i/2
        }else{
            break;//调整结束
        }
    }
}

void createHeap(){
    for(int i=n/2;i>=1;i--){
        downAdjust(i,n);
    }
}

void deletetop(){
    heap[1]=heap[n--]://用最后一个元素覆盖堆顶元素
    downAdjust(1,n);//向下调整堆顶元素
}
//添加堆顶元素,向上调整
void upAdjust(int low,int high){
    int i=high,j=i/2;
    while(j>=low){
        if(heap[j]

二叉树

二叉树静态结构

数组:给定一课包含(d为二叉树高度)的完全二叉树,如果把节点从上到下从左到右的编号为1,2,3,……则结点k的左右子节点编号分别为2k和2k+1。

二叉树动态结构

根据需要建立新的结点,然后将其组织成一颗树。

char s[maxn];
bool read_input(){
    failed=false;
    root=newnode();
    for(;;){
        if(scanf("%s",s)!=1) return false;//输入结束
        if(!strcmp(s,"()")) break;//读到结束标志,退出循环
        int v;
        sscanf(&s[1],"%d",&v);
        addnode(v,strchr(s,',')+1);//查找逗号,再插入结点。
    }
    return true;
    
}
\\二叉树结点和定义

struct Node{
    bool have_value;//初始false
    int v;
    Node *left,*right;//(NULL),注意这里用的也是指针
    //可以写一下初始化函数什么的
    public:
    void init(Node* left,int length,int id){//初始化方法
        this->next=next;
        this->weight=length;
    }
}
Node *root;//跟结点
-----------------------------------------
        node *newnode=new node();//新建节点,用的是指针
        newnode->init(1,b);
        current->next=newnode;

--------------------------------------------------
Node* newnode(){
   Node x= new Node();
    x.init();
    return x;//不知道可不可以
}


由于二叉树是递归定义的,其左右子节点类型都是指向结点类型的指针。因此结点类型为Node,其左右子节点类型是Node*

void addnode(int v,char* s){
    int n=strlen(s);
    Node* u=root;//根节点开始定义好了
    for(int i=0;ileft == NULL){
                node *newnode=new node();//新建节点,用的是指针
               newnode->init(1,b);
                
               u->left=newnode;
            }
            u=u->left;
        }else if(s[i]=='R'){
            if(u->right==NULL) u->right=newnode();
            u=u->right;
        }
        if(u->have_value) failed=true;//赋过值,表明输入有误
        u->v=v;
        u->have_value=true;//别忘记做标记。
        
    }
    
}
//层次遍历,这里使用bfs
bool bfs(vector& ans){//注意这里是引用!!!
    queue q;//队列装的是node指针
    ans.clear();//清空!!!
    q.push(root);
    while(!q.empty()){
        Node* u=q.front();
        if(!u->have_value) return false;//输入有误
        ans.push_back(u->v);
        if(u->left!=NULL) q.push(u->left);
        if(u->right!=NULL) q.push(u->right);
    }
    return true;
}

数组实现

主要note:多个属性就是多个数组

接下来,把所有的Node类型改成int类型,然后把结点结构中的成员变量改成全局数组
(例如,u->left和u->right分别改成left[u]和right[u]),除了char
外,整个程序就没有任何指
针了。

二叉树的递归遍历

对于二叉树T,可以递归定义它的先序遍历、中序遍历和后序遍历,PreOrder(T)=T的根节点+preOrder(T的左子树)+PreOrder(T的右子树)

InOrder(T)=InOder(T的左子树)+T的根节点+InOrder(T的右子树)

PostOrder(T)=PostOrder(T的左子树)+Postorder(T的右子树)+T的根节点。

前序遍历

void preorder(node* root){
    if(root == NULL){
        return;//到达空树,递归边界
    }
    //访问根节点root,例如将其数据域输出
    printf("%d\n",root->data);
    //访问左子树
    preorder(root->lchild);//左子树
    preorder(root->rchild);//右子树
}  
    

中序遍历

void inorder(node* root){
    if(root == NULL){
        return;//到达空树,递归边界
    }
    //访问根节点root,例如将其数据域输出
    inorder(root->lchild);//左子树
    printf("%d\n",root->data);
    //访问左子树
    
    inorder(root->rchild);//右子树
}

给定前序和中序得到二叉树

需要在中序序列中找到某个节点,使得,即最主要的是找到对应的子树的前序和中序,然后就能不停地遍历了。左子树的节点个数:k-1。左子树的先序序列区间[2,k],左子树中序序列区间[1,k-1],右子树的先序序列区间和中序序列区间都是[k+1,n]。

node* create(int preL,int preR,int inL,int inR){//注意这里返回的是指针
    if(preL>preR){
        return NULL;//先序序列长度小于等于0时,直接返回
    }
    node* root = new node();
    root->data=pre[preL];//前序开头是根节点
    int k;
    for(k=inL;k<=inR;k++){
        if(in[k]==pre[preL]){
            //在中序序列中找到in[k]==pre[L]的节点
            break;
            
        }
    }
    int numLeft=k-inL;//左子树的节点个数
    //则可知,左子树先序[preL+1,preL+numLeft],中序区间[inL,k-1]
    //返回左子树的根节点地址,赋值给root的左指针
    root->lchild=create(preL+1,preL+numLeft,inL,k-1);
    
    //右子树的先序区间为preL+numberLeft+1,preR],中序区间为[k+1,inR]
    root->rchild=create(preL+numLeft+1,preR,k+1,inR);
    
    return root;//返回根节点地址
}

给定后序序列和中序序列,如何构建二叉树

后序与前序的不同,notice,后序序列最后一个元素为根元素,所以倒着找就可以了,找到中序中的根元素,找到左右子区间

给定二叉树的中序遍历和后序遍历,可以构造出这棵二叉树。

#include 
#include 
#include 
#include 
using namespce std;

//因为各个结点的权值各不相同且都是正整数,直接用权值作为结点编号。
const int maxv=10000 + 10;
int in_order[maxv],post_order[maxv],lch[maxv],rch[maxv];

int n;

bool read_list(int *a){
    string line;
    if(!getline(cin,line)) return false;
    stringstream ss(line);
    n=0;
    int x;
    while(ss>>x) a[n++]=x;
    return n>0;
    
}
int build(int L1,int R1,int L2,int R2){
    if(L1>R1) return 0;//空树
    int root =post_order[R2];
    int p=L1;
    while(in_order[p]!=root) p++;
    int cnt=p-L1;//左子树结点个数
    lch[root]=built(L1,p-1,L2,L2+cnt-1);
    rch[root]=built(p+1,R1,L2+cnt,R2-1);
    return root;
}
int best, best_sum; //目前为止的最优解和对应的权和
void dfs(int u, int sum) {
sum += u;
if(!lch[u] && !rch[u]) { //叶子
if(sum < best_sum || (sum == best_sum && u < best)) { best = u; best_sum
= sum; }
}
if(lch[u]) dfs(lch[u], sum);
if(rch[u]) dfs(rch[u], sum);
}
int main() {
while(read_list(in_order)) {
read_list(post_order);
build(0, n-1, 0, n-1);
best_sum = 1000000000;
dfs(post_order[n-1], 0);
cout << best << "\n";
}
return 0;
}

你可能感兴趣的:(排序 & 堆 &二叉树)