程序员面试知识点超全整理之算法

原创不易,如果需要转载请附上本连接

算法

1. 反转链表

递归
使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 newhead
此后,每次函数在返回的过程中,让当前结点的下一个结点的 next->next 指针指向当前节点
同时让当前结点的 next 指针指向 NULL ,从而实现从链表尾部开始的局部反转
当递归函数全部出栈后,链表反转完成

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL or head->next == NULL) return head;
        ListNode* newhead = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return newhead;
    }
};

2. 多线程交替打印

线程同步之信号量 (sem_init,sem_post,sem_wait)
信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
不多做解释,要使用信号量同步,需要包含头文件 semaphore.h

主要用到的函数:
int sem_init (sem_t *sem, int pshared, unsigned int value); 其中sem是要初始化的信号量,pshared表示此信号量是在进程间共享还是线程间共享,value是信号量的初始值。

int sem_destroy (sem_t *sem); 其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。

int sem_wait (sem_t *sem); 等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。

int sem_post (sem_t *sem); 释放信号量,让信号量的值加1。相当于V操作。

#include <semaphore.h>
using namespace std;
class FizzBuzz {
private:
    int n;
    int cur;
    sem_t sem_fizz;
    sem_t sem_buzz;
    sem_t sem_fizz_buzz;//这三个信号量分别用于线程的同步
    sem_t sem_num;//用于触发数字增加

public:
    FizzBuzz(int n) {
        this->n = n;
        cur = 0;
        sem_init(&sem_fizz, 0, 0);
        sem_init(&sem_buzz, 0, 0);
        sem_init(&sem_fizz_buzz, 0, 0);//这三者的信号量初始化都置位为0,三个线程均被阻塞
        sem_init(&sem_num, 0, 1);//初始化信号量为1,触发开始
    }

    // printFizz() outputs "fizz".
    void fizz(function<void()> printFizz) {
        while(cur <= n){
            sem_wait(&sem_fizz);//等待信号量为1
            if(cur > n) break;
            printFizz();
            sem_post(&sem_num);
        }
    }

    // printBuzz() outputs "buzz".
    void buzz(function<void()> printBuzz) {
        while(cur <= n){
            sem_wait(&sem_buzz);
            if(cur > n) break;
            printBuzz();
            sem_post(&sem_num);
        }
        
    }

    // printFizzBuzz() outputs "fizzbuzz".
	void fizzbuzz(function<void()> printFizzBuzz) {
        while(cur <= n){
            sem_wait(&sem_fizz_buzz);
            if(cur > n) break;
            printFizzBuzz();
            sem_post(&sem_num);
        }
    }

    // printNumber(x) outputs "x", where x is an integer.
    void number(function<void(int)> printNumber) {
        while(++cur <= n){
            sem_wait(&sem_num);//开始时信号量为1,进入后面的操作
            if(cur % 3 == 0 && cur % 5 == 0){
                sem_post(&sem_fizz_buzz);
            }else if(cur % 3 == 0){
                sem_post(&sem_fizz);
            }else if(cur % 5 == 0){
                sem_post(&sem_buzz);
            }else{
                printNumber(cur);
                sem_post(&sem_num);
            }
        }
        sem_post(&sem_fizz);
        sem_post(&sem_buzz);
        sem_post(&sem_fizz_buzz);
    }
};

3. 实现单例模式

#include <iostream>

class Singleton
{
private:
    Singleton() { std::cout << "Constructor!" << std::endl; };
    Singleton(Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
public:
    ~Singleton() { std::cout << "Destructor!" << std::endl; };
    static Singleton& instance()
    {
        static Singleton ins;//静态局部变量,内存中只有一个,且只会被初始化一次
        return ins;
    }
    void Fun() { std::cout << "Fun" << std::endl; };//其他公有接口
};

程序员面试知识点超全整理之设计模式


4. 最小栈

public class 设计一个有gitMin的栈 {

    private Stack<Integer> stack = new Stack<Integer>();
    private int min;

    public void push(int x) {
        if (stack.isEmpty()) {
            min = x;
            stack.push(0);
        } else {
            // 计算差值
            int compare = x - min;
            stack.push(compare);
            // 如果差值小于0,显然 x 成为最小值,否则最小值不变
            min = compare < 0 ? x : min;
        }
    }

    public void pop() {
        int top = stack.peek();
        // 如果top小于0,显然最小值也一并会被删除,此时更新最小值
        min = top < 0 ? (min - top) : min;
        stack.pop();
    }

    public int getMin() {
        return min;
    }
 }

记一次面试:最小栈的最佳解法


5. 给定几十万个ip集合,判断任意一个ip是否属于这个集合

将ip地址转换成整数:

public static long IP2Long(string ip)
{
	string[] ipBytes;
	double num = 0;
	if(!string.IsNullOrEmpty(ip))
	{
		ipBytes = ip.Split('.');
		for (int i = ipBytes.Length - 1; i >= 0; i--)
		{
			num += ((int.Parse(ipBytes[i]) % 256) * Math.Pow(256, (3 - i)));
		}
	}
	return (long)num;
}

将所有ip集合转换成数字放到Map中,用Map的Find方法查找

判断IP地址是否在指定范围内的方法


6. 给定一个字符串,判断该字符串是否是环等的(字符串首位相连,如果能找到一个位置,从这个位置顺时针得到的字符串相等,即为环等)


7. 给定一个字符串,判断该字符串是否是环等的(字符串首位相连,如果能找到一个位置,从这个位置逆时针得到的字符串相等,即为环等)


8. 合并两个排序链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL)	return l2;
        if(l2 == NULL)	return l1;
        if(l1 == NULL && l2 == NULL) return NULL;
        ListNode* NewHead = NULL;
        if(l1->val > l2->val) {
            NewHead = l2;
            NewHead->next = mergeTwoLists(l1,l2->next);
        }else {
            NewHead = l1;
            NewHead->next = mergeTwoLists(l1->next,l2);
        }
        return NewHead;   
    }
};

合并两个排序的链表


9. 快排和归并的复杂度分析

快速排序和归并排序的时间复杂度分析——通俗易懂


10. 排序链表

class Solution {
public:
    ListNode* sortList(ListNode* head) 
    {
        if(head==nullptr || head->next==nullptr) return head;
        ListNode *slow = head, *fast = head, *pre = head;
        while(fast && fast->next)
        {
            //记录slow的前驱,针对链表有偶数个结点的情况
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }       
        pre->next = nullptr;
        //明确递归函数的返回结果是什么,sortList的返回结果就是已经排序号的链表的头节点;而merge的返回结果就是两个链表合并好之后的链表的头节点
        return merge(sortList(head), sortList(slow));
    }

    ListNode* merge(ListNode* l1,ListNode* l2)
    {
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        if(l1->val <= l2->val) {
            l1->next = merge(l1->next, l2);
            return l1;
        } else {
            l2->next = merge(l1, l2->next);
            return l2;
        }
    }
};

11. 二叉树中和为某一值的路径

前序遍历 + 递归回溯

我们用前序遍历来一次提取所有二叉树中从 root 到叶节点的路径,并把路径上节点的和与 sum 比较

class Solution {
private:
    vector<vector<int> > res;
    vector<int> temp;
public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if(!root) return {};
        recursion(root, sum);
        return res;
    }
    void recursion(TreeNode *root, int sum)
    {
        if(!root) return;
        temp.push_back(root -> val);
        sum -= root -> val;
        if(sum == 0 && !root -> left && !root -> right)
            res.push_back(temp);
        recursion(root -> left, sum); // 左
        recursion(root -> right, sum); // 右
        temp.pop_back(); // 函数退出之前先弹出当前节点
    }
};

12. 优势洗牌

13. 打家劫舍II

14. LFU 缓存

15. 乘积最大子数组

16. 车队

17. 用 Rand7() 实现 Rand10()

18. 大整数加法

19. 三数之和

20. k个不同整数的子数组

21. 快速查找 IP

22. 最小的k个数

23. 二叉搜索树的第k大节点

24. 数组中的重复数字

25. XML 格式解析

26. 有序数组找到第一个小于0的数和第一个大于0的数

27. 实现一个 string 类

28. 实现一个智能指针

29. 两个排序数组找中位数

30. 无重复字符的最长字串

31. string 转 float

七、其他

1. rebase 和 merge 用法(git)

2. 为了 B 站视频加载更快,可以怎么做

C++Primer 读书笔记,真干货!
c++11 标准的特性总结

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值