数据结构-字符串和数组简单题

大数加法

以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。

数据范围:len(s),len(t) \le 100000len(s),len(t)≤100000,字符串仅由’0’~‘9’构成
要求:时间复杂度 O(n)O(n)

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 计算两个数之和
 * @param s string字符串 表示第一个整数
 * @param t string字符串 表示第二个整数
 * @return string字符串
 */
function solve( s ,  t ) {
    // write code here
    if(s === '') return t;
    if(t === '') return s;
    let i = s.length - 1;
    let j = t.length - 1;
    let plus = 0; //是否进位
    let res = [];
    while(i>=0 || j>=0 || plus !== 0){
        let p = i>=0 ? s[i]-0:0;
        let q = j>=0 ? t[j]-0:0;
        let sum = p + q + plus;
        plus = sum >= 10 ? 1 : 0;
        let cur = sum % 10;
        res.push(cur);
        i--;
        j--;
    }
    return res.reverse().join('');
}
module.exports = {
    solve : solve
};

买卖股票的最好时机(一)输入:

[8,9,2,5,4,7,1]
返回值:
5
说明:
在第3天(股票价格 = 2)的时候买入,在第6天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第2天买入,第3天卖出,这样就亏损7了;同时,你也不能在买入前卖出股票。

function maxProfit( prices ) {
    if(prices.length <= 1) return 0; 
    let minvalue = prices[0];
    let max = 0;
    for(let i = 0; i < prices.length; i++){
        minvalue = Math.min(minvalue,prices[i]);
        max = Math.max(max,prices[i] - minvalue);
    }
    return max;
}
module.exports = {
    maxProfit : maxProfit
};

合并两个有序的数组

给出一个有序的整数数组 A 和有序的整数数组 B ,请将数组 B 合并到数组 A 中,变成一个有序的升序数组

/**
 * 
 * @param A int整型一维数组 
 * @param B int整型一维数组 
 * @return void
 */
function merge( A, m, B, n ) {
    let alen = m - 1;
    let blen = n - 1;
    let alllen = m + n - 1;
    while(alen >= 0 && blen >= 0){
        if(A[alen] >= B[blen]){
            A[alllen] = A[alen];
            alen--;
        }else{
            A[alllen] = B[blen];
            blen--;
        }
        alllen--;
    }
    while(blen >= 0){
        A[alllen] = B[blen];
        blen--;
        alllen--;
    }
}
module.exports = {
    merge : merge
};

第一个只出现一次的字符

在一个字符串中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

解题思路: 创建一个数组,数组初始值都为1,当存在重复的字符,把重复的字符的下标一起标记为2

function FirstNotRepeatingChar(str)
{
    let map = new Map();
    let arr = [];
    for(let i = 0; i< str.length;i++){
        arr[i] = 1;
        if(map.has(str[i])){
            let index = map.get(str[i]);
            arr[index] = 2;  //之前存储的相同值的元素
            arr[i] = 2;  //当前元素
        }else{
            map.set(str[i],i)
        }
    }
    return arr.indexOf(1);
    
}
module.exports = {
    FirstNotRepeatingChar : FirstNotRepeatingChar
};

有效括号序列

function isValid( s ) {
    let arr = {
        '(':')',
        '[':']',
        '{':'}',
    };
    let map = [];
    for(let i = 0; i < s.length; i++) {
        if(s[i] === '(' || s[i] === '[' || s[i] === '{') {
            map.push(arr[s[i]]);
        } else {
            if(!map.length || map.pop() !== s[i]) {
                return false;
            }
        }
    }
    return map.length === 0;
}
module.exports = {
    isValid : isValid
};

最长公共前缀

思路:以第一个元素作为参考基准,逐步遍历后面的元素

function longestCommonPrefix( strs ) {
    if(strs.length === 0) return '';
    let row = strs.length;
    let col = strs[0].length;
    for(let i = 0;i<col;i++){
        for(let j = 0;j<row;j++){
            let char = strs[0].charAt(i);
            if(strs[j].length === i || strs[j][i] !== char){
                return strs[0].substr(0,i);
            }
        }
    }
    return strs[0]
}
module.exports = {
    longestCommonPrefix : longestCommonPrefix
};

两数之和

描述:在一个数组中找到等于目标值的两个元素(只有一对),按顺序返回下标,下标从1开始

function twoSum( numbers ,  target ) {
    let map = new Map();
    let arr = [];
    for(let i = 0;i<numbers.length;i++){
        if(map.has(target - numbers[i])){
            arr[0] = map.get(target - numbers[i]) + 1;
            arr[1] = i + 1;
        }else{
            map.set(numbers[i],i);
        }     
    }
    return arr;
}
module.exports = {
    twoSum : twoSum
};

字符串变形

描述
对于一个长度为 n 字符串,我们需要对它做一些变形。

首先这个字符串中包含着一些空格,就像"Hello World"一样,然后我们要做的是把这个字符串中由空格隔开的单词反序,同时反转每个字符的大小写。

比如"Hello World"变形后就变成了"wORLD hELLO"。

function trans(s, n){
    let res = '';
    let tempStr = '';
    for(let i = 0 ; i < n ; i++){
        let char = s.charAt(i);
        if(char >= 'a' && char <= 'z'){
            tempStr += char.toUpperCase();
        }else if(char >= 'A' && char <= 'Z'){
            tempStr += char.toLowerCase();
        }else{
            tempStr = char + tempStr;
            res = tempStr + res;
            tempStr = '';
        }
    }
    res = tempStr + res;
    return res;
}

module.exports = {
    trans : trans
}

压缩字符串(一)

描述
利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2bc5a3。
1.如果只有一个字符,1不用写
2.字符串中只包含大小写英文字母(a至z)。

数据范围:
0<=字符串长度<=50000

要求:时间复杂度O(N)

三个数的最大乘积

描述
给定一个长度为 nn 的无序数组 AA ,包含正数、负数和 0 ,请从中找出 3 个数,使得乘积最大,返回这个乘积。

要求时间复杂度: O(n)O(n) ,空间复杂度: O(1)O(1) 。

数据范围:
3≤n≤10

思路:先排序,最大值产生在数组末端前三位和前端前两位与末端第一位的积之中

/**
 * 最大乘积
 * @param A int整型一维数组 
 * @return long长整型
 */
function solve( A ) {
    A = A.sort((a,b) => a-b);
    let len = A.length;
    let num1 = 1;  //定义变量,方便求积
    let num2 = 1;
    for(let i = len - 3;i < len; i++){
        num1 = num1 * A[i];
    }
    for(let i = 0 ; i < 2; i++ ){
        num2 = num2 * A[i];
    }
    num2 = num2 * A[len - 1];
    return Math.max(num1,num2);
}
module.exports = {
    solve : solve
};

旋转字符串

描述
字符串旋转:
给定两字符串A和B,如果能将A从中间某个位置分割为左右两部分字符串(可以为空串),并将左边的字符串移动到右边字符串后面组成新的字符串可以变为字符串B时返回true。

例如:如果A=‘youzan’,B=‘zanyou’,A按‘you’‘zan’切割换位后得到‘zanyou’和B相同,返回true。
再如:如果A=‘abcd’,B=‘abcd’,A切成‘abcd’和’’(空串),换位后可以得到B,返回true。

function solve( A ,  B ) {
    if(A.length !== B.length) return false;
    let str = A + A;
    if(str.indexOf(B) !== -1) return true;
    return false
}
module.exports = {
    solve : solve
};

二进制取反

描述
有一个二进制字符串numnum,可以选择该串中的任意一段区间进行取反(可以进行一次或不进行),取反指将00变为11,将11变为00。那么取反之后的可能的最大的字典序

function maxLexicographical( num ) {
    let flag = -1;
    let right = num.length;
    for(let i = 0;i<num.length;i++){
        if(num[i] == '0' && flag == -1){  //确定要取反的左边界
            flag = i;
        }
        if(num[i] == '1' && flag != -1){
            right = i;
            //确定右边界后,要退出循环
            break;
        }
    }
    if(flag == -1) return num
    //该区间内需要取😡
    for(let i = flag; i < right ;i++){
        num[i] = '1';
    }
    return num;
}
module.exports = {
    maxLexicographical : maxLexicographical
};

杨辉三角

给定一个非负整数 num ,生成杨辉三角的前 num 行。
杨辉三角中,每个数是左上方和右上方的数之和。

function generate( num ) {
    let arr = [];
    for(let i = 0; i < num; i++){
        arr.push([]);
        if(i === 0){
            arr[0][0] = 1;
        }else{
            for(let j = 0;j<=i;j++){
                let num1 = j === 0 ? 0 : arr[i-1][j-1];  //左边界
                let num2 = j === i ? 0 : arr[i-1][j];   //右边界
                arr[i][j] = num1 + num2;
            }
        }
    }
    return arr;
}

最小时间差

给定一个 24 小时制(小时:分钟 “HH:MM”)的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。

输入:timePoints = [“23:59”,“00:00”]
输出:1

/**
 * @param {string[]} timePoints
 * @return {number}
 */
// var findMinDifference = function(timePoints) {
//     timePoints.map(item => {
//         let arr = [];
//         timePoints.forEach((item,i) => {
//             arr[i] = parseInt(item.substring(0,2) * 60) + parseInt(item.substring(3));
//         })
//         arr.sort((a,b) => a - b);
//         let min = Number.MIN_VALUE;
//         for(let i = 1; i < arr.length; i++){
//             min = Math.min(min,arr[i] - arr[i-1]);
//         }
//         return Math.min(min,arr[0] + 1440 - arr[arr.length - 1])
//     })
// };

var oneday = 60 * 24;

var getMinutes = (time) => {
    const t = time.split(':');
    return Number(t[0]) * 60 + Number(t[1]);
};

var findMinDifference = function(timePoints) {
    if (timePoints.length > oneday) {
        return 0;
    }

    const arr = timePoints.sort();
    let min = getMinutes(arr[0]) + oneday - getMinutes(arr[arr.length-1]);
    for (let i=0; i<arr.length-1; i++) {
        if (arr[i] === arr[i+1]) {
            return 0;
        }
        const sub = getMinutes(arr[i+1]) - getMinutes(arr[i]);
        min = Math.min(min, sub);
    }
    return min;
};

跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标
数组中的每个元素代表你在该位置可以跳跃的最大长度
判断你是否能够到达最后一个下标

var canJump = function(nums) {
    if(nums.length === 1) return true;
    let step = nums[0];
    for(let i = 1;i < nums.length-1 ;i++){
        if(i <= step){
            step = Math.max(step,i+nums[i]);
        }
    }
    return step >= nums.length - 1;
};

合并区间

var merge = function(intervals) {
    intervals.sort((a,b) => a[0]-b[0]);
    let prev = intervals[0];
    let result = [];
    for(let i = 0;i<intervals.length;i++){
        let cur = intervals[i];
        if(cur[0] > prev[1]){
            result.push(prev);
            prev = cur;
        }else{
            prev[1] = Math.max(cur[1],prev[1]);
        }
    }
    // 最后得把当前添加到result
    result.push(prev);
    return result;
};

爬楼梯问题

动态规划
var climbStairs = function(n) {
    let dp = [];
    dp[0] = 1;
    dp[1] = 1;
    for(let i = 2;i<=n;i++){
        dp[i] = dp[i-1] + dp[i-2];
    }
    return dp[n];
};

不同路径

动态规划

机器人每次只能往右或者往下走一步,从一个方格表左上走到右下的的路径数

var uniquePaths = function(m, n) {
    let result = [];
    for(let i = 0;i < m; i++){
        result.push([]);
        // 第一列填充为1
        result[i][0] = 1;   
    }
    for(let j = 0;j < n; j++){
        // 第一行填充为0
        result[0][j] = 1;
    }
    for(let i = 1;i < m; i++){
        for(let j = 1;j < n; j++){
            result[i][j] = result[i-1][j] + result[i][j-1];
        }
    }
    return result[m-1][n-1]
};

最小路径和

动态规划

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。

思路:右下角等于上或座的数字+当前的数字
当是第一行:则是左边的数字+当前数字
当是第一列:则是上面的数字+当前数字

/**
 * @param {number[][]} grid
 * @return {number}
 */
var minPathSum = function(grid) {
    // O(n) 和 O(1)
    for(let i = 0 ;i < grid.length;i++){
        for(let j = 0;j < grid[i].length ;j++){
            if(i === 0 && j !== 0){
                grid[i][j] = grid[i][j-1] + grid[i][j];
            };
            if(i !== 0 && j === 0){
                grid[i][j] = grid[i-1][j] + grid[i][j];
            };
            if( i !== 0 && j !== 0){
                grid[i][j] = Math.min(grid[i-1][j],grid[i][j-1]) + grid[i][j];
            }
        }
    }
    return grid[grid.length-1][grid[0].length-1]
};

颜色分类

sort排序
双指针 一次遍历

只循环1遍,双指针p0指向数组开头,p2指向数组结尾
只要p0<=p2
把p2指针及之前所有的2交换到后面去,交换一位p2指针往前移1位
确保p0之前所有的元素都是0

var sortColors = function(nums) {
    var p0 = 0;
    var p2 = nums.length - 1;
    for(let i = 0 ; i <= p2 ; i++){
        // 把2换到最后 逻辑推算出:不用担心p2处也是2
        while(i < p2 && nums[i] === 2){
            [nums[i],nums[p2]] = [nums[p2],nums[i]];
            p2--;  
        }
        // 把0换到前面
        if(nums[i] === 0){
            [nums[p0],nums[i]] = [nums[i],nums[p0]];
            p0++;
        }
    }
    return nums;
};

二叉树中序遍历

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var inorderTraversal = function(root) {
    let res = [];
    // 递归
    const inorder = (root) => {
        if(!root) return;
        inorder(root.left);
        res.push(root.val);
        inorder(root.right);
    }
    inorder(root);
    return res;
};

二叉树最大深度

深度优先遍历

每一层级等于下一层级的最大深度+1,直到最后为null即为0;

var maxDepth = function(root) {
    if(!root){
        return 0; 
    }
    let leftDepth = maxDepth(root.left);
    let rightDepth = maxDepth(root.right);
    return Math.max(leftDepth,rightDepth) + 1;
};

判断是否为对称二叉树

左右两边完全对称

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function(root) {
    var check = (p,q) => {
        if(!p && !q) return true;
        if(!p || !q) return false;
        return p.val === q.val && check(p.left,q.right) && check(p.right,q.left);
    }
    return check(root,root);
};

找到数组中消失的数字

给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果

思路:遍历nums,每遇到一个数 xx,就让 nums[x−1] 增加 n。由于nums 中所有数均在 [1,n]中,增加以后,这些数必然大于 n。最后我们遍历nums,若nums[i] 未大于 n,就说明没有遇到过数 i+1。这样我们就找到了缺失的数字

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDisappearedNumbers = function(nums) {
    const len = nums.length;
    // 将原数组假想为一个哈希表 空间复杂度O(1)
    for(const num of nums){
        let n = (num - 1) % len;
        nums[n] += len ; 
    }
    let res = [];
    for(let i = 0 ; i < len ;i++){
        if(nums[i] <= len){
            res.push(i + 1);
        }
    }
    return res;
};

找到数组中只出现一次的元素

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

按位异或操作符

一个值和0进行按位异或操作所得为该值,相同的两个值进行异或操作,所得为0(甲 按位异或 0 得 甲,甲 按位异或 甲 得 0)。

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let result = 0;
    for(const num of nums){
        result ^= num;
    }
    return result;
};

多数元素

摩尔投票法
/**
 * @param {number[]} nums
 * @return {number}
 */
var majorityElement = function(nums) {
    // 摩尔投票法 多数元素只有1个
    let count = 0;
    let candidate = nums[0];
    for(const num of nums){
        if(count === 0) candidate = num;
        count += candidate === num ? 1 : -1;
    }
    return candidate;
};

无重复字符的最长子串

滑动窗口
var lengthOfLongestSubstring = function(s) {
    if(s.length === 0) return '';
    let j = 0 ;
    let max = 0;
    let map = new Map();
    for(let i = 0;i < s.length;i++){
        if(map.has(s.charAt(i))){
            // 举例子理解
            j = Math.max(j,map.get(s.charAt(i)) + 1);
            // j = map.get(s.charAt(i)) + 1;
        }
        map.set(s.charAt(i),i);
        max = Math.max(max,i-j+1);
    }
    return max;
};

最长回文子串

对于字符串来说,每一个字符都可以作为回文串的中心点(回文串的中心点的意思是从中间向两边扩散开来)
既然每个一字符都可以作为回文串的中心点,回文串又有奇数回文串和偶数回文串
遍历字符串的每个字符,分别对奇数回文串和偶数回文串取最长的

var longestPalindrome = function(s) {
    let paliString = (s,left,right) => {
        while(left >= 0 && right <= s.length && s[left] === s[right]){
            // 向两边喊开
            left--;
            right++
        }
        //此处截取的长度为 right - 1 - (left + 1) + 1
        return s.substr(left + 1,right - left - 1);
    }
    let res = '';
    for(let i = 0 ; i < s.length ; i++){
        // 奇回文子串
        let s1 = paliString(s,i,i);
        // 偶回文子串
        let s2 = paliString(s,i,i+1);
        res = s1.length > res.length ? s1 : res;
        res = s2.length > res.length ? s2 : res;
    }
    return res;
};

环形链表

快慢指针必在环内相遇
var hasCycle = function(head) {
    let fast = head;
    let slow = head;
    if(head === null || head.head === null) return false;
    while(fast.next && fast.next.next){
        fast = fast.next.next;
        slow = slow.next;
        if(fast === slow) return true;
    }
    return false;
};

返回环形链表环的起始节点

哈希set
//O(n) O(n)
var detectCycle = function(head) {
    const visited = new Set();
    while (head !== null) {
        if (visited.has(head)) {
            return head;
        }
        visited.add(head);
        head = head.next;
    }
    return null;
};
快慢指针
//O(n) O(1)
var detectCycle = function(head) {
    if(head === null || head.next === null) return null;
    let fast = head;
    let slow = head;
    let pic = null;
    while(fast.next !== null && fast.next.next !== null){
        fast = fast.next.next;
        slow = slow.next;
        if(fast.next === null) return null;
        if(fast === slow){
            pic = head;
            while(pic !== slow){
                pic = pic.next;
                slow = slow.next;
            }
            return pic;
        };
    }
    return null;
};

返回两个链表的相交节点

双指针
//O(m+n) O(1)
var getIntersectionNode = function(headA, headB) {
    let p1 = headA;
    let p2 = headB;
    while(p1 !== p2){
        p1 = p1 === null ? headB : p1.next;
        p2 = p2 === null ? headA : p2.next;
    }
    return p1;
};

删除链表倒数第n个节点

节点长度>=n

var removeNthFromEnd = function(head, n) {
    let fast = head;
    let slow = head;
    for(let i = 0 ; i < n ; i++){
        fast = fast.next;
    }
    if(fast === null) return head.next;
    while(fast.next !== null){
        slow = slow.next;
        fast = fast.next;
    }
    slow.next = slow.next.next;
    return head;
    
    // let count = 0;
    // while(fast !== null && fast.next !== null){
    //     count++;
    //     fast = fast.next;
    //     if(count === n){
    //         while(fast !== null && fast.next !== null){
    //             slow = slow.next;
    //             fast = fast.next;
    //         }
    //         slow.next = slow.next.next;     
    //     }
    // }
    // return head;
};

合并两个有序链表

var mergeTwoLists = function(l1,l2) {
    // let dummy = new ListNode(-1);
    // let prev = dummy;
    // while(list1 !== null && list1 !== null){
    //     if(list1.val <= list2.val){
    //         dummy.next = list1;
    //         list1 = list1.next;
    //     }else{
    //         dummy.next = list2;
    //         list2 = list2.next;
    //     }
    //     dummy = dummy.next;
    // }
    // dummy.next = list1 === null ? list2 : list1;
    // return prev.next;
    const prehead = new ListNode(-1);
    let prev = prehead;
    while (l1 != null && l2 != null) {
        if (l1.val <= l2.val) {
            prev.next = l1;
            l1 = l1.next;
        } else {
            prev.next = l2;
            l2 = l2.next;
        }
        prev = prev.next;
    }
    prev.next = l1 === null ? l2 : l1;
    return prehead.next;
};

反转

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值