算法随手笔记

1.c++比较与java,可以把main函数写在前面,自定义函数写在后面嘛?

不行,c++必须先定义函数才能进行使用

2.关于map的遍历方法

C++:

使用iterator迭代器完成遍历

#include<iostream>
 #include<map>

 using namespace std;

 int main() {
     map<int,int> mi;
     map<int,int>::iterator it;
     //mi.insert(1,2);
     mi[2]=4;
     //mi.insert(3,6);

     for(it=mi.begin();it!=mi.end();it++) {
         cout<<it->first<<":"<<it->second<<endl;
     }
     return 0;
 }

这里注意注释的地方,不能直接使用insert(int,int)来添加数据。

java:

public static void main(String[] args) {
 
 
  Map<String, String> map = new HashMap<String, String>();
  map.put("1", "value1");
  map.put("2", "value2");
  map.put("3", "value3");
  
  //推荐,尤其是容量大时
  System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
}

3.深度学习二分查找:

基本代码,寻找key的索引值,当不存在与数组时返回-1

#include<iostream>

 using namespace std;

 int BiraySearch(int arr[],int key,int left,int right) {
     if(left<=right) {
         //要点1
         int mid=left + (right-left)/2;
         if(arr[mid] == key) {
             return mid;
         } else if (arr[mid] > key) {
             //要点2
             return BiraySearch(arr,key,left,mid-1);
         }
         return BiraySearch(arr,key,mid+1,right);
     }
     return -1;
 }

 int main() {
     int a[5]={2,5,10,100,200};
     int x=10;
     int num=sizeof(a)/sizeof(a[0]); //要点3
     cout<<BiraySearch(a,x,0,num-1)<<endl;
     return 0;
 }

要点1:mid的表达式也可以写成(left+right)/2,但是这样可能会引起溢出

要点2:在if判断中,我们判断的是left<=right,所以是左闭右闭去间,在发现mid不是key的索引值时,直接跳过mid搜索mid-1或者mid+1即可

            在当if是left<right时,在发现mid不是key的索引值时,left的变化区间还是[mid+1,right),而right的变化区间则是[left,mid)

要点3:在c++中不像java一样,在获取数组的元素个数时直接使用length即可,我们要先取数组的sizeof值,然后再除以数组一个元素的sizeof值,即可得到个数

缺陷:在基本代码中,在我们的数组存在重复数时,当我们需要寻找目标数的最左或者最右的索引值时,我们需要怎么办呢?

寻找左侧边界的二分搜索

#include<iostream>

 using namespace std;

 int BiraySearch(int arr[],int key,int left,int right) {
     while(left<right) {
         int mid=left + (right-left)/2;
         if(arr[mid]>=key) {
             right=mid;        //要点1
         } else {
             left=mid+1;
         }
     }
     //要点2
     if(left==0) return -1;
     return (key==arr[left]) ? left : -1;
 }

 int main() {
     int a[6] = {1,1,2,2,2,2};
     int n=2;
     int num=6;        //要点3
     cout<<BiraySearch(a,n,0,num)<<endl;
     return 0;
 }

要点1:我们要找最左侧得目标数,所以很自然是缩小right值,然后前面就说明了为啥是mid

要点2:要考虑目标数不存在与数组中得情况,当left=0或者循环完后arr[left]都不等与目标数时

要点3:在函数BiraySearch中,while循环判断是left<right,所以right值就不用减一

寻找右侧边界的二分查找

 #include<iostream>

 using namespace std;

 int BiraySearch(int arr[],int key,int left,int right) {
     //if(arr.empty()) return 0;
     while(left<right) {
         int mid=left + (right-left)/2;
         if(arr[mid]==key) {
             left=mid+1;
         } else if(arr[mid] > key) {
             right=mid;
         } else {
             left=mid+1;
         }
     }
     if(left==0) return -1;
     return (key==arr[left-1]) ? left-1 : -1;    //要点1
 }

 int main() {
     int a[6] = {1,1,2,2,2,2};
     int n=2;
     int num=6;
     cout<<BiraySearch(a,n,0,num)<<endl;
     return 0;
 }

要点1:我们对left得处理是mid+1,所以可能mid==key,但是mid+1不等于,可是这时已经跳出循环了。这样得话我们在循环体外就得-1再判断是否为目标数

这里我用的是数组来进行代码编写,但是使用vector容器来进行是最好的,因为可以直接判断是否为空数组,是则直接返回即可

 

4.c++中的null空指针怎么表示,或者表示一个空vector,map方法

与Java不用的是,java可以使用null表示空指针,空vector,map。在c++中,我们只能使用NULL表示空指针,在表示空vector,map时,我们可以自定义创建一个无用的vector,map来进行返回

5.什么是线性时间复杂度?

时间复杂度为线性阶O(n),同理可得常数时间复杂度O(1),对数时间复杂度O(log2n),线性对数时间复杂度O(nlog2n),平方时间复杂度O(n^2),立方时间复杂度O(n^3)

6.在c++中如果要使用python中的set函数要怎么使用,头文件是什么?

c++提供unordered_set<> ,头文件为<unordered_set>

7.负数的位运算要怎么计算?

先把负数转化成unsigned类型即可计算。例如 int 类型转化成 unsigned int 型

8.记住几个关键位置的ascii值

‘0’ = 48,‘A’ = 65 , ‘a’ = 97;

9.判断是否为字母的isalpha()和判断是否为大小写的isupper()的头文件是?

ctype.h或者cctype

10.字母大小写转化的函数

tolower(char) :转化成小写 , toupper:转化成大写

11.java和c++中的string.erase的区别

java:str.erase(i , j) 从i到j,删除 j - i 个char值,即 [ i , j) 个char值

c++:str.erase(i , j) 从i 开始删除 j 个char值

12.引用 & 与复制

引用就是用另一个变量来代替该变量,当引用变量的值发生变化时,原变量也会发生变化。但是复制变量发生值得改变也不会影响原变量

13.引用作为函数参数的特点

  1. 被调函数中对形参变量的操作就是对其相应目标对象(在主调函数中)的操作
  2. 在内存中并没有产生实参的副本,直接在实参上进行操作,传递参数的效率更高,所占内存也更少
  3. 在被调函数中同样要给形参分配内存单元。在主调函数的调用点处,必须用变量的地址作为实参,所以引用更加容易使用,更清晰

14.深度优先与广度优先搜索的深度学习:一下分析都是以树为例子

原理:

深度优先搜索(Depth First Search, 简称 DFS):从根节点到叶节点,当到某一叶节点时,就返回上一步,搜索有无其他叶子路径。直到全部的叶节点都遍历完

广度优先搜索(Breadth First Search,简称 BFS):从左到右对每一层的树节点进行遍历,直到全部遍历完

代码实现:

DFS:

因为DFS的搜索方式是从上往下的,所以有两种方法:

1.递归法:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
void DFS(TreeNode *root) {
    if(root == NULL)     return ;
    cout<<root-<val<<endl;
    DFS(root->left);
    DFS(root->right);
}

上面的递归法使用的是前序遍历方式,中后序方法一样。

2.存栈法:

public static void DFSWithStack(TreeNode root) {
    if (root == null) {
        return;
    }

    stack<Node> stack = new Stack<>();
    // 先把根节点压栈
    stack.push(root);
    while (!stack.isEmpty()) {
        TreeNode treeNode = stack.top();
        stack.pop();
        // 遍历节点
        process(treeNode)

        // 先压右节点
        if (treeNode->right != null) {
            stack.push(treeNode.right);
        }

        // 再压左节点
        if (treeNode->left != null) {
            stack.push(treeNode.left);
        }
    }
}

思路如下:

  1. 对于每个节点来说,先遍历当前节点,然后把右节点压栈,再压左节点(这样弹栈的时候会先拿到左节点遍历,符合深度优先遍历要求)
  2. 弹栈,拿到栈顶的节点,如果节点不为空,重复步骤 1, 如果为空,结束遍历。

BFS:

因为BFS是从左到右遍历的,所以递归法并不适用。可以使用队列来完成操作:

/**
 * 使用队列实现 bfs
 * @param root
 */
void BFS(TreeNode root) {
    if (root == null) {
        return;
    }
    queue<TreeNode> q;
    q.push(root);

    while (!q.isEmpty()) {
        TreeNode node = q.front();
        q.pop();
        System.out.println("value = " + node.value);
        TreeNode left = node->left;
        if (left != null) {
            q.push(left);
        }
        TreeNode right = node->right;
        if (right != null) {
            q.push(right);
        }
    }
}

15.deque的用法?

双指针的queue用法,可以在头部插入,也可以在尾部插入

16.stoi和to_string的头文件

都是#include<string>

17.拓扑排序

概念:

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边∈E(G),则u在线性序列中出现在v之前。

算法流程

  1. 想遍历节点,把节点入度为0得节点分离出来,然后把这些节点得邻接节点入度减一
  2. 重复步骤1,直到全部节点都分离出来
  3. 检查是否还存在入度不为0得节点,若没有则说明没有环

练习题:

207. 课程表

 

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> res(numCourses , vector<int>());
        vector<int> innum(numCourses);

        for(int i = 0; i < prerequisites.size(); i++) {
            innum[prerequisites[i][0]]++;
            res[prerequisites[i][1]].push_back(prerequisites[i][0]);
        }
        queue<int> q;
        for(int i = 0; i < numCourses; i++) {
            if(innum[i] == 0) {
                q.push(i);
            }
        }
        int cnt = 0;
        while(!q.empty()) {
            int tmp = q.front();
            q.pop();
            cnt++;
            for(int i = 0; i < res[tmp].size(); i++) {
                innum[res[tmp][i]]--;
                if(innum[res[tmp][i]] == 0)     q.push(res[tmp][i]);
            }
        }
        return cnt == numCourses;
    }
};

210. 课程表 II

 

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph(numCourses , vector<int>());
        vector<int> indegrees(numCourses , 0) , res;

        for(int i = 0; i < prerequisites.size(); i++) {
            indegrees[prerequisites[i][0]]++;
            graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
        }
        //定义优先队列
        priority_queue<int, vector<int>, greater<int>> q;
        for(int i = 0; i < numCourses; i++) {
            if(indegrees[i] == 0) {
                q.push(i);
            }
        }
        while(!q.empty() && res.size() < numCourses) {
            int temp = q.top();
            q.pop();
            res.push_back(temp);
            for(int i = 0; i < graph[temp].size(); i++) {
                indegrees[graph[temp][i]]--;
                if(indegrees[graph[temp][i]] == 0)  q.push(graph[temp][i]);
            }
        }
        if(res.size() == numCourses) {
            return res;
        } else  return {};
    }
};

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值