字节跳动算法整理

Posted yan1345

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字节跳动算法整理相关的知识,希望对你有一定的参考价值。

无重复字符的最长子串

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int m[256] = {0}; //存储每个字符在字符串的位置
        res = 0, left = 0;//结果和左边界
        for(int i=0;i<s.size();i++){
            if(m[s[i]] == 0 || m[s[i]]<left){
                res = max(res, i-left+1);//更新res
            }
            else{//如果s[i]重复出现且s[i]第一次出现的位置在s[left,i]之间
                left = m[s[i]]; //更新left坐标
            }
            m[s[i]] = i+1;
        }
        return res;
    }
};

最长公共前缀 *

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(strs.size() == 0) 
            return "";
        string s = strs[0];//取出第一个字符串,首字母
        for(int i = 1; i< strs.size(); i++){ //从第2个字符串,遍历
            while(strs[i].find(s) != 0){ //index ==0 字符串开头
                s = s.substr(0,s.size()-1);
                if(s.empty())
                    return "";
            }
        }
        return s;
    }
};

简化路径

本题思路较为简单,采取入栈的方法。先只考虑"/"之间的字符串:

  • 当遇到不是"."".."时的字符串,则将当前元素入栈;
  • 当遇到".."且栈不为空时,则出栈顶部元素;
  • 其他情况不做处理。

问题的关键在于如何识别一个和多个"/",利用string流的办法,先把"/"替换成空格,之后将string绑定到一个istringstream中,通过类似 cin 的方法提取内容 while(cout >> string)

class Solution {
public:
    string simplifyPath(string path) {
        if(path == "/")
            return "/";
        //将字符串中的‘/‘替换为空格
        for(int i = 0; i < path.size(); i++){
            if(path[i] == ‘/‘)
                path[i] = ‘ ‘;
        }
        vector<string> stack;    //声明栈,存放两个‘/‘之间的字符串
        istringstream str(path); //声明并绑定一个输入流
        string buf;              //输入流读入的临时缓存
        while(str >> buf){
            if(buf == ".." && !stack.empty() ) //遇到".."且栈不为空则出栈
                stack.pop_back();
            else if(buf != "." && buf != "..") //不是"."和".."的字符串则入栈
                stack.push_back(buf);
        }
        if(stack.empty()) //简化结果为空则返回根目录
            return "/";
        string res;       //存放结果
        for(int i = 0; i < stack.size(); i++){
            res = res + "/" + stack[i];
        }
        return res;
    }
};

复原IP

class Solution {
public:
    vector<string> res;
    vector<string> restoreIpAddresses(string s) {
        string temp = "";
        dfs(s,temp,0);
        return res;
    }
    //参数 s 字符串递归减小,temp 返回值路径,最后一个参数控制word数目
    void dfs(string s,string &temp, int word_number)
    {
        if(word_number==4)
        {
            //cout << temp << "	";
            if(s.empty()) res.push_back(temp);
        }else{
            if(word_number > 0) temp += ‘.‘;
            for(int i = 1; i <=3 && i <= s.length();i++)
            {
                if(valid(s.substr(0,i))){
                	//注意截取操作函数 s.substr(o,i)从pos0截取i个字符
                    temp += s.substr(0,i);
                    //s变短。试探后
                    dfs(s.substr(i,s.length()-i), temp , word_number+1);
                    //回溯i个字符 注意temp不断变长 所以回溯不是 0~i 而是回溯 length - i
                    temp = temp.substr(0,temp.length()-i);
                }
            }
            temp.pop_back();
        }
    }
    bool valid(const string &s){
        if(s.empty()||(s[0]==‘0‘ && s.size()>1)) return false;
        int val = stoi(s);
        if(val >= 0 && val <= 255) return true;
        return false;
    }
};

三数之和

? 先排序

? 以数组中每个元素为三个数中的第一个,在其后找到满足条件的剩下2个

– 因为已经排序,为了避免重复,所以相同元素只处理一次(也就是说,如果nums[i] == nums[i - 1],说明以nums[i]为第一个元素的情况已经处理过,那么跳过,继续处理下一个数)

? 为了找出剩余2个满足要求target = 0 - nums[i]的数,使用两个指针l和r,令sum = nums[l] + nums[r]

– 如果sum > target,则将r向左移动至第一个不等的元素处

– 如果sum < target,则将l向右移动至第一个不等的元素处

– 否则,满足要求,添加到结果中,并将l和r都移动至第一个不等的元素处

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        if(nums.size() < 3) 
            return vector<vector<int>>();
        
        sort(nums.begin(),nums.end());
    
        vector<vector<int>> res;
        for(int i = 0;i < nums.size() - 2;i++){
            if(i == 0 || nums[i] != nums[i - 1]){
                int target = 0 - nums[i];
                int l = i + 1,r = nums.size() - 1;
                while(l < r){
                    int sum = nums[l] + nums[r];
                    if(sum < target){
                        while(l < r && nums[l + 1] == nums[l])    l++;
                        l++;
                    }
                    else if(sum > target){
                        while(l < r && nums[r - 1] == nums[r])    r--;
                        r--;
                    }  
                    else{
                        vector<int> triplet = {nums[i],nums[l],nums[r]};
                        res.push_back(triplet);
                        while(l < r && nums[l + 1] == nums[l])    l++;
                        while(l < r && nums[r - 1] == nums[r])    r--;
                        l++;r--;
                    }
                }
            }
        }
        
        return res;
    }
};

岛屿的最大面积

给定一个包含了一些 01 的非空二维数组 grid

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

当遇到1时,使用DFS求该岛屿的面积,然后与最大值比较,如果更大则更新最大值。为了防止重复求相同岛屿的面积,在DFS过程中,将1设置为0
class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        if(grid.size() == 0)
            return 0;
        
        int res = 0;
        int rows = grid.size(),columns = grid[0].size();   
        for(int i = 0;i < rows;i++){
            for(int j = 0;j < columns;j++){
                if(grid[i][j] == 1){
                    int area = DFS(grid,i,j,rows,columns);
                    res = area > res ? area : res; 
                }
            }
        }
        
        return res;
    }
    
    int DFS(vector<vector<int>> &grid,int row,int column,int rows,int columns){
        //越界或者不连通
        if(row < 0 || row >= rows || column < 0 
        || column >= columns || grid[row][column] == 0)
            return 0;
        
        int count = 1;
        grid[row][column] = 0;//防止重复计算
        /*遍历该顶点的四个方向*/
        count += DFS(grid,row - 1,column,rows,columns);
        count += DFS(grid,row + 1,column,rows,columns);
        count += DFS(grid,row,column - 1,rows,columns);
        count += DFS(grid,row,column + 1,rows,columns);
        
        return count;
    }
};
 

搜索旋转排序数组

给你一个整数数组 nums ,和一个整数 target

该整数数组原本是按升序排列,但输入时在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0,r = nums.size() - 1;
        while(l <= r){
            int mid = (l + r) / 2;
            if(nums[mid] == target) return mid;
            if(nums[l] < nums[r]){
                if(nums[mid] < target)   
                    l = mid + 1;
                else                     
                    r = mid - 1;
            }
            else{
                if(nums[mid] >= nums[l]){//mid在左边
                    if(target > nums[r] && target < nums[mid])  
                        r = mid - 1;
                    else    
                        l = mid + 1;
                }
                else{//mid在右边
                    if(target > nums[mid] && target < nums[l])  
                        l = mid + 1;
                    else    
                        r = mid - 1;
                }
            }
        }
        return -1;
    }
};

朋友圈

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

提示:

  • 1 <= N <= 200

  • M[i][i] == 1

  • M[i][j] == M[j][i]

    
    

接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

技术图片

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length

  • 0 <= n <= 3 * 104

  • 0 <= height[i] <= 105

  • 使用两个变量max_left和max_right,分别表示左边柱子的最高者和右边柱子的最高者,初始化为最左边柱子的高度和最右边柱子的最高者
    另外使用两个指针left_bar和right_bar,分别表示左边的一个柱子和右边的一个柱子,初始化为最左边的柱子和最右边的柱子。现在不考虑中间的柱子怎么样,只分析这两个指针所表示的柱子
    假设left_bar和right_bar中高度较低者为left_bar:
    ?如果left_bar比max_left要低,那么left_bar上面能储水max_left-left_bar
    ?否则,left_bar比左边的柱子都要高,那么需要更新max_left的值
    经过上面的处理后,递增left_bar,继续处理下一个柱子
    同理,如果高度较低者为right_bar:
    ?如果right_bar比max_right要低,那么right_bar上面能储水max_right-right_bar
    ?否则,right_bar比右边的柱子都要高,那么需要更新max_right的值
    经过上面的处理后,递减right_bar,继续处理下一个柱子
    这里可能有个疑问:为什么增加储水量时不考虑另外一个边界?原因在于每次更新max_left或max_right时,都是left_bar和right_bar中高度较低者,因此,另外一边必然存在一个边界大于max_left或max_right

class Solution {
public:
   int trap(vector<int>& height) {
       int water = 0,sz = height.size();
       int left_bar = 0,right_bar = sz - 1;
       int max_left = 0,max_right = 0;
       
       while(left_bar < right_bar){
           if(height[left_bar] < height[right_bar]){
               if(height[left_bar] < max_left)
                   water += max_left - height[left_bar];
               else    
                   max_left = height[left_bar];
               left_bar++;
           }
           else{
               if(height[right_bar] < max_right) 
                   water += max_right - height[right_bar];
               else    
                   max_right = height[right_bar];
               right_bar--;
           }
       }
       
       return water;
   }
};
//时间复杂度:O(n)  空间复杂度:O(1)

反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

1)迭代

每次循环将curr.next修改为前一节点

因为需要将当前节点的next节点修改为前一节点,所以需要用一个指针prev记录前一节点。同时,由于修改了当前节点的next节点后,无法访问原来的next节点,因此需要一个指针next,在修改前记录原来的next节点

? 时间复杂度:O(n)

? 空间复杂度:O(1)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *curr = head,
        *next = head ? head->next : NULL,
        *prev = NULL;
        while(curr){
            curr->next = prev;
            prev = curr;
            curr = next;
            if(next)
                next = next->next;
        }
        
        return prev;
    }
};

递归

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

两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
//?可以在新链表头结点之前创建一个辅助节点来便利处理
//?要注意链表处理完后可能还有进位,此时还需创建一个节点
class Solution {
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
        ListNode preHead(0), *p = &preHead;
        int extra = 0;
        while (l1 || l2 || extra) {
            int sum = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + extra;
            extra = sum / 10;
            p->next = new ListNode(sum % 10);
            p = p->next;
            l1 = l1 ? l1->next : l1;
            l2 = l2 ? l2->next : l2;
        }
        return preHead.next;
    }
};

合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
  2->6
  ]
  将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
class Solution {
public:
   ListNode* merge(ListNode* l1, ListNode* l2) {
        if (l1 == NULL) return l2;
        if (l2 == NULL) return l1;
        ListNode* pre = new ListNode(0);
        ListNode* cur = pre;
        while (l1 && l2) {
            if (l1->val <= l2->val) {
                cur->next = l1;
                l1 = l1->next;
            } else {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        cur->next = l1 != NULL ? l1 : l2;
        return pre->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
         if (lists.size()== 0) return NULL;
        // 分组间隔每次扩大两倍
        int len = lists.size();
        int interval = 1;
        while (interval < len) {
            // 根据当前间隔,两两合并,合并后的结果保存在两个链表的第一个
            for (int i = 0; i < len - interval; i += 2 * interval) {
                lists[i]  = merge(lists[i], lists[i + interval]);
            }
            interval *= 2;
        }

        return lists[0];
    }
};

买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = INT_MAX,sell,maxprofit = 0;
        for(int i = 0;i < prices.size();i++){
            if(prices[i] < buy) 
                buy = prices[i];
            else if(prices[i] > buy){ 
                sell = prices[i];       
                if(sell - buy > maxprofit)  maxprofit = sell - buy;
            }
        }
        return maxprofit;
    }
};

买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() <= 1)  return 0;

        vector<int> dp(3,0);
        vector<int> local(3,0);
        
        for(int i = 1;i < prices.size();i++)
            for(int k = 2;k > 0;k--){
                local[k] = prices[i] - prices[i - 1] + max(dp[k - 1],local[k]);
                dp[k] = max(dp[k],local[k]);
            }
        
        return dp[2];
    }
};

最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
    	int n = nums.size();
        if(n == 0) return 0;
        
        int tempsum = nums[0], maxsum = nums[0];
        for(int i=1;i<n;i++)
        {
        	if(tempsum < 0) {	  //tempsum < 0时,代表对之后的结果无增益 
        	   tempsum = nums[i]; //更新tempsum
			}else{
			   tempsum += nums[i];
			}
			
			maxsum = tempsum > maxsum ? tempsum : maxsum;
        }
        
        return maxsum;
	}
};

最小栈

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) —— 将元素 x 推入栈中。

  • pop() —— 删除栈顶的元素。

  • top() —— 获取栈顶元素。

  • getMin() —— 检索栈中的最小元素。

    class Solution {
    public:
        void push(int value) {
            dataStack.push(value);
            if(minStack.empty() || !minStack.empty() && value < minStack.top()){
                minStack.push(value);
            }
            else
                minStack.push(minStack.top());
        }
        void pop() {
            if(dataStack.empty())
                throw new exception();
            dataStack.pop();
            minStack.pop();
        }
        int top() {
            if(dataStack.empty())
                throw new exception();
            return dataStack.top();
        }
        int min() {
            if(dataStack.empty())
                throw new exception();
            return minStack.top();
        }
    private:
        stack<int> dataStack;
        stack<int> minStack;
    };
    

LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制

实现 LRUCache 类:

  • LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

class LRUCache {
public:
    LRUCache(int capacity) : hashtable() , ls() , cap(capacity) , curr(0) {}
    
    int get(int key) {
        if(hashtable.find(key) == hashtable.end())  return -1;
        auto itr = hashtable[key];
        if(itr == ls.begin())
            return itr->second;
        else{
            ls.push_front(pair<int,int>(itr->first,itr->second));
            auto new_itr = ls.begin();
            hashtable[key] = new_itr;
            ls.erase(itr);
            return ls.front().second;
        }
        return 1;
    }
    
    void put(int key, int value) {
        if(hashtable.find(key) != hashtable.end()){
            ls.erase(hashtable[key]);
            ls.push_front(pair<int,int>(key,value));
            auto new_itr = ls.begin();
            hashtable[key] = new_itr;
            return;
        }
        if(curr == cap){
            hashtable.erase(ls.back().first);
            ls.pop_back();
            curr--;
        }    
        ls.push_front(pair<int,int>(key,value));
        auto new_itr = ls.begin();
        hashtable[key] = new_itr;
        curr++;
    }
private:
    unordered_map<int,list<pair<int,int>>::iterator> hashtable;
    list<pair<int,int>> ls;
    int cap;
    int curr;
};

x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

#include<iostream>
using namespace std;
int main()
{
        double eps = 1e-6;
        double k = 0.8;  // 输入正数
        double l = 0.0,r,mid;
        if (k>=1) r = k;  // 若输入正数大于1,则右端点设为 k
        if (k<1)  r = 1;  // 若输入整数小于1,则右端点设为 1

    while (fabs(l-k/l)>eps)
    {
        mid = l + (r - l) /2 ;   
        if (mid<k/mid)
        {
            l = mid;
        }
        else {
            r = mid;
        }
    }
    cout << l << endl;
    return 0;
}

UTF-8 编码验证

UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则:

  1. 对于 1 字节的字符,字节的第一位设为0,后面7位为这个符号的unicode码。
  2. 对于 n 字节的字符 (n > 1),第一个字节的前 n 位都设为1,第 n+1 位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

这是 UTF-8 编码的工作方式:

   Char. number range  |        UTF-8 octet sequence
      (hexadecimal)    |              (binary)
   --------------------+---------------------------------------------
   0000 0000-0000 007F | 0xxxxxxx
   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

给定一个表示数据的整数数组,返回它是否为有效的 utf-8 编码。

注意:
输入是整数数组。只有每个整数的最低 8 个有效位用来存储数据。这意味着每个整数只表示 1 字节的数据。

class Solution {
public:
    bool validUtf8(vector<int>& data) {
        int size = data.size();
        for(int i = 0; i < size; i++)
        {
           int bitCount = 0;
           for(int j = 7; j > 0; j--)
           {
               if(((data[i] >> j) % 2) == 0)
                   break;
               bitCount ++;
           }
           if(bitCount == 1 || bitCount > 4)
               return false;
           if(bitCount + i > size)
               return false;
           for(int j = 0; j < bitCount - 1; j++)
               if(((data[i + j + 1] >> 7) % 2) == 0 
                  || ((data[i + j + 1] >> 6) % 2) != 0)
                   return false;
           if(bitCount != 0)
               i += bitCount - 1;
        }
        return true;
    }
};

第二高的薪水

编写一个 SQL 查询,获取 Employee 表中第二高的薪水(Salary) 。

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+

例如上述 Employee 表,SQL查询应该返回 200 作为第二高的薪水。如果不存在第二高的薪水,那么查询应返回 null

+---------------------+
| SecondHighestSalary |
+---------------------+
| 200                 |
+---------------------+
SELECT (SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 1) AS secondHighestSalary

strcmp

//字符串比较
int strcmp(const char *str1,const char *str2)
{
    assert(str1 && str2);
    //在判断是否相等时不一定要转换为unsigned char
    while((*str1 == *str2) && *str1){
        str1++;
        str2++;
    }
    if(*str1 == *str2)//说明上面循环退出时*str等于0
        return 0;
    //128种扩展ascii码使用最高位来标识,
    //所以在判断返回大于0还是小于0是,要转换为unsigned char,否则结果相反
    return *(unsigned char*)str1 > *(unsigned char*)str2 ? 1 : -1;
}

strcat

//字符串拼接
char* strcat(char *strDest,const char *strSrc)
{
    assert(strDest && strSrc);
    char *p = strDest;
    while(*p) 
        p++;
    while(*p++ = *strSrc++);
    return strDest;
}

strlen

//字符串长度计算
int strlen(const char *str)
{
    assert(str);
    int len = 0;
    while(*str++)   
        len++;
    return  len;
}
//字符串长度计算
int strlen(const char *str)
{
    assert(str);
    return (*str == 0) ? 0 : 1 + strlen(str + 1);  
}

memcpy

void* memcpy(void *dst,const void *src,size_t size)
{
    if(dst == NULL || src == NULL){
        return NULL;
    }
    char *pdst = (char*)dst;
    char *psrc = (char*)src;
    //有重叠,从高地址开始复制
    if(pdst > psrc && pdst < psrc + size){
        pdst = pdst + size - 1;
        psrc = psrc + size - 1;
        while(size--){
            *pdst-- == *psrc--;
        }
    }
    //没有重叠,从低地址开始复制
    else{
        while(size--){
            *pdst++ = *psrc++;
        }
    }
    return dst;
}

strcpy

//字符串拷贝
char* strcpy(char *strDest,const char *strSrc)
{
    assert(strDest && strSrc);

    char *p = strDest;
    while(*p++ = *strSrc++);
    return strDest;
}












以上是关于字节跳动算法整理的主要内容,如果未能解决你的问题,请参考以下文章

最新整理:字节跳动历年Java中高级面试题全收录!后台开发JAVA岗

字节跳动算法工程师总结:腾讯+字节+阿里面经真题汇总,含面试题+答案

字节跳动二面面试官问:JVM垃圾回收器和回收算法,我全靠这个逆天改命!

仅一年工作经验成功跳槽字节跳动,腾讯并拿到字节的offer,全靠这份面经!

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

字节跳动算法工程师总结:mysql安装和客户端配置教程