算法笔记重要算法

1.堆的调整,建立,删除,添加

#include <iostream>

using namespace std;


//首先要明白堆是一种完全二叉树,所以存储堆最好用数组。数组i号的左孩子就是2i,右孩子就是2i+1

const int maxn = 100;
//heap为堆,n为元素个数
int heap[maxn],n=10;

//对heap在low,high范围进行向下调整,其中low为欲调整结点的数组下标,high为堆最后一个元素的数组下标
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;                                  //让j存储右孩子下标

        if(heap[j]>heap[i])     //如果孩子中最大的权值比欲调整结点i大(此时heap[j]一定是最大权值的孩子)
        {
            swap(heap[j],heap[i]);
            i = j;              //保持i依旧为欲调整结点,j为i的左孩子
            j= i*2;
        }
        else
            break;              //孩子的权值比欲调整结点i小,调整结束
    }
}


void createHeap()               //建堆的代码,注意i从n/2开始
{
    for(int i = n/2;i>=1;i++)
        downAdjust(i,n);
}

void deleteTop()                //删除堆顶元素,所以要用最后一个元素覆盖堆顶元素
{
    heap[1] = heap[n--];        //用最后一个元素覆盖堆顶元素,并让元素个数减1
    downAdjust(1,n);            //向下调整堆顶元素
}


void upAdjust(int low,int high)
{
    int i = high,j = i/2;       //i为欲调整结点,j为其父亲
    while(j >= low)             //父亲在low,high的范围内
    {
        if(heap[j]<heap[i])                 //如果父亲的权值小于欲调整结点i的权值
        {
                swap(heap[j],heap[i]);      //交换父亲结点和想要调整的结点
                i = j;                      //保持i为欲调整的结点,j为i的父亲
                j = i/2;
        }
        else
            break;                          //父亲结点比欲调整的结点权值还要大,调整结束
    }
}


void Insert(int x)                          //在堆末尾插入新元素,让元素个数加1,然后将数组末尾赋值为x,然后向上调整新加入的结点
{
    heap[++n] = x;
    upAdjust(1,n);
}




void heapSort()                             //堆排序算法,步骤依次是:
{
    creatHeap();                            //1,建堆
    for(int i =n;i>1;i--)                   //2.倒着枚举,直到堆中只有一个元素
    {
        swap(heap[i],heap[1]);              //3.交换heap[i]与堆顶
        downAdjust(1,i-1);                  //4.调整堆顶
    }
}

2.二叉树查找相关操作

#include <iostream>

using namespace std;

void search(node* root,int x)
{
    if(root == NULL)
    {
        printf("search failed!");
        return;
    }

    if(root -> data == x)
    {
        printf("search successfully!");
        return;
    }
    else if(root->data < x)
        search(root->rchild,x);
    else
        search(root->lchild,x);
}


void newnode(int x)
{
    node*root = new node;
    root->data=x;
    root->lchild=NULL;
    root->rchild=NULL;
}



void Insert(node* &root,int x)              //记得要加引用符号,因为二叉树的结构发生了改变
{
    if(root == NULL)
    {
        node* root =newnode(x);
        return
    }
    if(root->data ==x)
        return;
    else if(root->data <x)
    {
        Insert(root->rchild,x);
    }
    else
        Insert(root->lchild,x);
}



node*Create(int data[],int n)               //根据data数组的元素插入n个结点,跟建树的原理完全一致
{
    node* root = NULL;
    for(int i=0;i<n;i++)
    {
        Insert(root,data[i]);
    }
    return root;
}




//二叉查找树的删除比较复杂,这里先定义两个求最大最小结点的函数,以便寻找二叉查找树的前驱和后继
node* findmax(node* root)                       //找最大结点
{
    while(root->rchild != NULL)
        root = root -> rchild;
    return root;
}


node* findmin(node* root)                       //找最小结点
{
    while(root->lchild != NULL)
        root = root -> lchild;
    return root;
}



void deleteNode(node* &root,int x)              //在二叉查找树中删除权值为x 的结点
{
    if(root == NULL)    return;
    if(root->data == x)
    {
        if(root->rchild == NULL&&root ->lchild ==NULL)
            root = NULL;                        //把root地址设为NULL,父节点就引用不到它了
        else if(root->lchild != NULL)
        {
            node *pre = findmax(root);
            root->data=pre->data;               //根结点被前驱覆盖,然后递归删除前驱
            deleteNode(root->lchild,pre->data);                 //往根结点左子树递归删除前驱
        }
        else
        {
            node *next = findmin(root);
            root->data=next->data;
            deleteNode(root->rchild,next->data);                //往根结点右子树递归删除后继
        }
    }
    else if(root->data <x)
        deleteNode(root->rchild);
    else
        deleteNode(root->lchild);
}


3.二叉树各种遍历以及按遍历顺序构建二叉树


using namespace std;

struct node
{
    typename data;
    node* lchild;
    node* rchild;
};

void preorder(node* root)               //先序遍历
{
    if(root ==NULL)
        return;
    printf("%d\n",root->data);
    preorder(root->lchild);
    preorder(root->rchild);
}


void inorder(node* root)                //中序遍历
{
    if(root ==NULL)
        return;
    inorder(root->lchild);
    printf("%d\n",root->data);
    inorder(root->rchild);
}



void postorder(node* root)              //后序遍历
{
    if(root ==NULL)
        return;
    postorder(root->lchild);
    postorder(root->rchild);
    printf("%d\n",root->data);
}


#include <queue>

void LayerOrder(node* root)
{
    queue<node*> q;             //这里队列元素类型是地址,里面的成员都是地址,为了取出来的时候方便改变对应结点的元素
    q.push(root);
    while(!q.empty())
    {
        node* now = q.front();
        q.pop();
        printf("%d\n",now->data);
        if(now->lchild != NULL)
            q.push(now->lchild);
        if(now->rchild != NULL)
            q.push(now->rchild);
    }
}


//--------------------------------------------------------
//如果要计算每个结点的层次,需要在二叉树结点的结构体添加一个记录layer的变量
struct node1
{
    int data;
    int layer;                                  //层次
    node* lchild;
    node* rchild;
};



//需要在根结点入队前就令根结点的layer为1表示根结点是第一层,(也可以是0,看题目),之后在now->lchild,now->rchild入队前把它们的层号都记为now的层号+1
void LayerOrder1(node* root)
{
    queue<node*> q;                     //注意队列是存地址
    root->layer =1;                     //根结点的层号为1
    q.push(root);
    while(!q.empty())
    {
        node* now = q.front();
        q.pop();
        if(now->lchild != NULL)
        {
            now->lchild->layer=now->layer+1;
            q.push(now->lchild);
        }
        if(now->rchild != NULL)
        {
            now->rchild->layer=now->layer+1;
            q.push(now->rchild);
        }
    }
}



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;          //左子树的结点个数,因为k是中序遍历根结点,那么k到inL就是左子树

    root->lchild = create(preL+1,preL+numLeft,inL,k-1);
    //左子树的先序区间为[preL+1,preL+numLeft],中序区间为[inL,k-1]
    //返回左子树的根结点地址,赋值给root的左指针

    root->rchild = create(preL+numLeft+1,preR,k+1,inR);
    //原理同上

    return root;


}

4.二叉树定义创建插入

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;

struct node
{
    typename data; //数据域
    node* lchild;  //指向左子树根结点的指针
    node* rchild;  //指向右子树根结点的指针
};


node* newNode(int v)            //新建一个数据域为x的结点
{
    node* Node = new node;      //申请一个node变量的地址空间(指针),可以用malloc但是很麻烦
    Node->data = v;
    Node->lchild = NULL;
    Node->rchild = NULL;
    return Node;
}


void Search(node* root ,int x,int newdata)
{
    if(root == NULL)
        return ;                                //空树,死胡同,递归边界
    if(root -> data == x)
        root -> data = newdata;                 //找到数据域为x的结点,把它的数据域改成newdata
    Search(root->lchild,x,newdata);
    Search(root->rchild,x,newdata);
}



void Insert(node* &root,int x)                  //上面的search不加引用是因为只修改指针root指向的内容而不是指针root本身,
{                                               //这里为了使新结点接到二叉树上面,需要使用root=new node语句,所以要改变root这个地址的值
    if(root == NULL)                            //如果函数需要新建节点,或者改变二叉树的结构,那么要加引用,如果仅仅是遍历,或者修改已有结点的内容就不引用
    {
        root = newNode(x);
        return;                                 //空树,说明查找失败,也即插入位置(递归边界)
    }
    if(/*根据情况而定插入的方式,此处假设是插入左子树*/)
        Insert(root->lchild,x);
    else
        Insert(root->rchild,x);

}

node* Create(int data[],int n)                  //为了方便这里把建树需要的数据存储在数组中
{
    node* root =NULL;
    for(int i=0;i<n;i++)
        Insert(root,data[i]);
    return root;
}

5.二分查找

#include <iostream>
#include <cstdio>
using namespace std;

//没有重复元素的二分查找,返回对应元素的下标
int binarySearch(int A[],int left,int right,int x)      //给定查找目标数组,左右端点和查找元素
{
   int mid;                                             //mid为 left和 right的中点
   while(left <= right)                                 //如果left>right就没办法形成闭区间了
   {
        mid = (left + right)/2;                         //取中点
       if(A[mid] == x) return mid;                      //找到x,返回下标
       else if(A[mid] > x)                              //中间的数大于 x
           right = mid - 1;                             //往左子区间 [left,mid-1] 查找
       else                                             //中间的数小于x
           left = mid + 1;                              //往右子区间 [mid+1,right]查找
   }
   return -1;                                           //查找失败,返回 -1
}



//有重复元素的二分查找,最终返回第一个大于等于x元素的位置(可以不存在,不存在就返回假设应该存在的位置)
//left right传入的初值为 [0,n],之所以取n是因为查询的元素可能比序列所有元素都大
int lower_bound1(int A[],int left,int right,int x)
{
    int mid;                                //mid 为 left 和right的中点
    while(left < right)                     //对[left ,right]来说,left == right意味着找到唯一位置
    {
        mid = (left + right) / 2;           //取中点
        if(A[mid] >= x)                     //中间的数大于等于x
                                            //如果把>=改成>,最终返回的就是第一个大于x的元素的位置,而不是大于等于x




            right = mid;                    //往左子区间[left,mid]查找
        else                                //中间的数小于x
            left = mid + 1;                 //往右子区间[mid + 1,right]查找
    }
    return left;                            //返回夹出来的位置
}



//二分法拓展一之          求近似值(根号2)
const double eps = 1e-5;    //精度为10的-5次方
double f(double x)          //计算f(x)
{
    return x*x;
}

double calSqrt()
{
    double left=1,right=2,mid;          //在[left,right] = [1,2]范围内找
    while(right - left > eps)
    {
        mid = (left + right) / 2;
        if(f(mid) > 2)
            right = mid;                //往右逼近
        else
            left = mid;                 //往左逼近
    }
        return mid;
}

6.排序综合

#include <stdio.h>
#include <stdlib.h>
#define maxn 100



//  选择排序
void SelectSort(int *A,int n)
{
    for(int i = 1;i <= n;i ++)          // A[1]-----A[n]所以下标是1----n,更方便主函数的使用
    {
        int k = i;                      // 选出[i,n]中最小的元素,下表为k
        for(int j =i;j <= n;j ++)
        {
            if(A[j] < A[k])
                k = j;
        }                               // 目的是找到最小元素的下标
        int temp = A[i];                // 交换A[k] , A[i]
        A[i] = A[k];
        A[k] = temp;
    }
}


//-------------------------------------------------------------------------------------------------------------------
//  插入排序
int A[maxn],n;                          //n为元素个数,数组下标为1-n
void insertSort()
{
    for(int i =2;i <= n;i ++)           //进行n - 1趟排序
    {
        int temp =A[i],j = i;           //temp 临时存放A[i],j从i开始往前枚举
        while(j > 1&& temp < A[j-1])
        {
            A[j]=A[j-1];                //把A[j-1]后移一位至A[j]
            j--;
        }
        A[j]=temp;                      //插入位置为j
    }
}


//-------------------------------------------------------------------------------------------------------------------
//归并排序递归实现
//将数组的A的[L1,R2],[L2,R2]区间合并为有序区间 (此处L2即为 R1 + 1)
void merge(int A[],int L1,int R1,int L2,int R2)
{
    int i = L1,j = L2;                          //i指向 A[L1],j指向 A[L2]
    int temp[100],index = 0;                    //temp临时存放合并后的数组, index为其下标(注意最后一行的i初始为0)
    while(i <= R1 && j <= R2)
    {
        if(A[i] <= A[j])
            temp[index++] = A[i++];
        else
            temp[index++] = A[j++];
    }                                           //字面意思,不用解释吧
    while(i <= R1) temp[index++] = A[i++];
    while(j <= R2) temp[index++] = A[j++];
    for(i = 0;i < index;i ++)
        A[L1 + i] = temp[i];                    //将合并后的序列赋值回数组A
}

void mergeSort(int A[],int left,int right)
{
    inf(left < right)                           //只要left小于 right
    {
        int mid = (left + right) / 2;           //取[left,right]的中点
        mergeSort(A,left,mid);                  //递归,将左子区间[left,mid]归并排序
        mergeSort(A,mid+1,right);               //递归,将右子区间[mid + 1,right]归并排序
        merge(A,left,mid,mid+1,right);          //将左子区间和右子区间合并
    }
}


//-------------------------------------------------------------------------------------------------------------------
//归并排序非递归实现
//暂时不想写,有点复杂







//--------------------------------------------------------------------------------------------------------------------
//快排(快速排序,应用了two pointers的原理)
//非递归算法
int Partition(int A[],int left,int right)           //left right分别为序列首尾下标
{                                                   //只是一趟快排,结果大致有序而已
    int temp = A[left];
    while(left < right)
    {
        while(left < right && A[right] > temp) right --;
        A[left] = A[right];
        while(left < right && A[left] < temp) left ++;
        A[right] = A[left];
    }
    A[left] = temp;
    return left;                                //返回相遇的下标
}


//递归算法
void quickSort(int A[],int left,int right)
{
    if(left < right)                            //当前区间的长度超过1
    {                                           //将[left,right]按A[left]一分为二
        int pos = Partition(A,left,right);
        quickSort(A,left,pos-1);                //对左子区间递归进行快速排序
        quickSort(A,pos+1,right);               //对右子区间递归进行快速排序
    }
}

7.最短路径

#include <iostream>

using namespace std;

//---------------------------------------------------------------------------------------迪杰斯特拉伪代码
//G为图,一般设为全局变量,数组d为源点到达各点的最短路径长度,s为起点
Dijkstra(G,d[],s)
{
    //初始化
    for(循环n次)
    {
        u = 使d[u]最小的还未被访问的顶点的标号;
        记u已经被访问;
        for(从u出发能到达的所有顶点v)
        {
            if(v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优)
                优化d[v];
        }
    }
}

//-----------------------------------------------------------------------------------------邻接矩阵版本
const int MAXV=1000;                    //最大顶点数
const int INF=100000000;                //一个不可能的值,代表两点之间没有形成通路,即相邻结点还没被访问

                                        //邻接矩阵适用于点数不大的情况
int n,G[MAXV][MAXV];                    //n为顶点数,maxv为最大顶点数
int d[MAXV];                            //存储起点到各点的最短路径长度
bool vis[MAXV]={false};                 //标记数组,代表还没访问


void Dijkstra(int s)                    //s为起点
{
    fill(d,d+MAXV,INF);                 //fill函数将整个d数组赋为INF(此处不应该用memset)
    d[s]=0;                             //起点s到自身的距离为0
    for(int i=0;i<n;i++)
    {
        int u=-1,MIN=INF;               //u使得d[u]最小,MIN存放最小的d[u]
        for(int j=0;j<n;j++)            //找到未访问的顶点中d[]最小的
            if(vis[j]==false&&d[j]<MIN)
            {
                u=j;
                MIN=d[j];
            }
        //找不到小于INF的d[u],说明剩下的顶点和起点s都不连通
        if(u == -1)return;
        vis[u] = true;                  //标记u已访问
        for(int v=0;v<n;v++)            //如果v未访问&&  u能到达v  &&以u为中介点可使d[v]更优
        {
            if(vis[v] == false&& G[u][v] != INF &&d[u] +G[u][v]<d[v])
                d[v] = d[u] + G[u][v];    //优化d[v]
        }
    }
}



//----------------------------------------------------------------------------------------邻接表版本
struct Node
{
    int v,dis;                      //v为边的目标顶点,dis为边权
};

vector<Node>Adj[MAXV];              //图G,Adj[u]存放从顶点u出发可以到达的所有顶点
int n,d[MAXV];                      //n为顶点数,图G使用邻接表实现,MAXV为最大顶点数,d存放起点到达各点
bool vis[MAXV] = {false};           //标记数组,初始所有顶点都没访问

void Dijkstra(int s)
{
    fill(d,d+MAXV,INF);             //将整个d数组赋值为INF(慎用memset)
    d[s] = 0;                       //起点s到达自身的距离为0
    for(int i =0;i<n;i++)
    {
        int u = -1,MIN= INF;        //u使d[u]最小,MIN存放该最小的d[u]
        for(int j=0;j<n;j++)
        {
            if(vis[j] == false&& d[j] < MIN)
            {
                u = j;
                MIN=d[j];
            }
        }
        if(u == -1) return;         //u还是-1说明找不到联通的点
        vis[u] = true;              //标记u为已访问
        for(int j=0;j<Adj[u].size();j++)
        {
            int v = Adj[u][j].v;    //通过邻接表直接获得u能到达的顶点v
            if(vis[v] == false && d[u] + Adj[u][j].dis < d[v])          //如果v未访问&&以u为中介点可以使d[v]更优
                d[v] = d[u] + Adj[u][j].dis;                            //优化d[v]
        }
    }
}

8.深度遍历

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

//深度遍历的单顶点递归和全图递归的伪代码

DFS(u)
{
    vis[u] = true;                      //设置u已经被访问
    for(从u出发能到达的所有顶点v)       //枚举从u出发能到达的所有顶点v
        if(vis[v] == false)             //若v未被访问
            DFS(v);                     //递归访问x
}

DFSTraver(G)
{
    for(G的所有顶点u)           //对G的所有顶点u
        if(vis[u] == false)     //如果u未被访问(注意这里是vis[u]上面是vis[v])
        DFS(u);                 //访问u所在的连通块
}



//正式代码
const int MAXV = 1000;                          //最大顶点数
const int INF  = 1000000000;                    //设INF为一个很大的数,即一个不可能的数,如果边值等于它说明两个节点不联通

//---------------------------------------------------------------------------------------------------------------------------邻接矩阵版DFS
int n,G[MAXV][MAXV];                            //n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false};                       //如果顶点i已被访问,则vis[i]=true,初值为false

void DFS(int u,int depth)                       //u为当前访问的顶点标号,depth为深度
{
    vis[u]=true;                                //设置u已被访问
    //如果需要对u做一些操作可以在这里进行
    //下面对所有从u出发能到达的分支顶点进行枚举
    for(int v=0;v<n;v++)
    {
        if(vis[v]==false&&G[u][v]!=INF)         //如果v未被访问,且u可到达v
            DFS(v,depth+1);                     //访问v,深度加1
    }
}


void DFSTrave()                                 //遍历图G
{
    for(int u=0;u<n;u++)                        //对每个顶点u
        if(vis[u]==false)                       //如果u未被访问
            DFS(u,1);                           //访问u和u所在的连通块,1表示初始第一层
}






//--------------------------------------------------------------------------------------------------------------------------邻接表版DFS
vector<int >Adj[MAXV];                          //图G的邻接表,每一个容器Adj[i].push_back(j)存放起点i,终点u的顶点编号u
int n;                                          //n为顶点数,MAXV为最大顶点数
bool vis[MAXV]={false};                         //如果顶点i已被访问,则vis[i]==true.初值为false
void DFS(int u,int depth)                       //u为当前访问的顶点标号,depth为深度
{
    vis[u]=true;                                //设置u已被访问
    //如果需要对u进行一些操作,可以在此处进行
    for(int i=0;i<Adj[u].size();i++)            //对u出发可以到达的所有顶点
    {
        int v = Adj[u][i];
        if(vis[v] == false)
            DFS(v,depth+1);
    }
}


void DFSTrave()                                 //对每个顶点u,若u未被访问,则访问u和u所在的连通块,1表示初始第一层
{
    for(int u=0;u<n;u++)
        if(vis[u]==false)
        DFS(u,1);
}

9.广度遍历


using namespace std;


//----------------------------------------------------------------------------------伪代码
BFS(u)
{
    queue<typename> q;
    q.push(u);                              //u入队
    inq[u] = true;                          //设置u已经被加入过队列
    while(!q.empty())                       //只要队列非空
    {
        //取出q的队首元素访问
        for(从u出发可到达的所有顶点v)
            if(inq[v] == false)
            {
                q.push(v);
                inq[v] = true;
            }
    }
}


BFSTrave(G)
{
    for(G所有顶点u)
    {
        if(inq[u] == false)
            BFS(u);
    }
}


//-----------------------------------------------------------------------------------邻接矩阵版
int n,G[MAXV][MAXV];                            //n为顶点数,MAXV为最大顶点数
bool inq[MAXN] = {false};                       //作为顶点i是否入过队的标记数组
void BFS(int u)
{
    queue<int >q;
    q.push(u);
    inq[u] = true;
    while(!q.empty())
    {
        int u = q.front;
        q.pop();
        for(int v = 0;v < n;v++)
            if(inq[v] == false&&G[u][v] !=INF)          //如果顶点v没访问而且跟u之间有路径
        {
            q.push(v);
            inq[v] = true;
        }
    }
}


void BFSTrave()
{
    for(int u = 0;u <n;u++)
    {
        if(inq[u] == false)
            BFS(q);
    }
}

//-----------------------------------------------------------------------------------------邻接表版
vector<int>Adj[MAXV];                   //图G,Adj[u]存放从顶点u出发可以到达的所有顶点
int n;                                  //n为顶点数,MAXV为最大顶点数
bool inq[MAXV] = {false};               //若顶点i曾入过队列,则inq[i] == true,初值为false

void BFS(int u)
{
    queue<int >q;
    q.push(u);
    inq[u] = true;
    while(!q.empty())
    {
        int u = q.front;
        q.pop();
        for(int i = 0;i < Adj[u].size();i++)                //枚举从u出发能到达的所有顶点
        {
            int v = Adj[u][i];                              //v是u能到达的一个顶点
            if(inq[v] == false)
            {
                q.push(v);
                inq[v] = true;
            }
        }

    }
}


void BFSTrave()
{
    for(int u = 0;u<n;u++)
        if(inq[u] == false)
            BFS(q);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值