剑指1-数组/字符串/链表

剑指offer刷题记录

一,数组

1. 数组中重复的数字-剑指offer面试3

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

一般关于数组,常有思路先排序再操作,这题也一样.除此之外,还有其他思路如下:

解法1: 哈希表或者新数组装出现过的数字, 空间复杂度O(N),时间复杂度O(N)

/* 时间复杂度O(N), 空间复杂度O(N) */
var findRepeatNumber = function(nums) {
    
    
    var arr = [];
    for (var i = 0; i < nums.length; i++) {
    
    
        if (arr.indexOf(nums[i]) == -1) {
    
    
            arr.push(nums[i]);
        } else {
    
    
            console.log(nums[i]);
        }
    }
}

解法2: 利用题目中的条件:所有数字都在 0~n-1 的范围内, 所以按照下标排序, 比如0就放在nums[0], 这样如果有重复的, 比较nums[i]和nums[i+1]即可

/* 时间复杂度O(N), 空间复杂度O(1) */
var findRepeatNumber = function(nums) {
    
    
    for (let i = 0; i < nums.length; i++) {
    
    
        var n = 0;
        var temp;
        //while一不小心就写出了死循环,加个跳出机制比较好调试
        while (i != nums[i]) {
    
    
            n++;
            if (n > 10) {
    
    
                break;
            }
            if (nums[nums[i]] == nums[i]) {
    
    
                return nums[i];
            } else {
    
    
                //因为nums[nums[i]]依赖于nums[i]决定下标,所以必须在nums[i]修改前修改nums[nums[i]]
                temp = nums[nums[i]];
                nums[nums[i]] = nums[i];
                nums[i] = temp;
            }
        }
    }
}

2.二维数组中的查找-剑指offer面试4

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。

解法1: 我的解法是基于暴力法的遍历, 不过遍历之前比较了target是否在该行的左端点和右端点之间

/* 时间复杂度最坏为O(n*2),空间复杂度O(1) */
var findNumberIn2DArray = function(matrix, target) {
    
    
    //注意这里用matrix[0]判断数组为空而不是matrix,因为matrix即使为空,也依然是一个对象,既然是对象,那么判断语句就自动为true,除非不存在,就像[]的matrix[0]不存在.
    while (matrix[0]) {
    
    
        let n = matrix.length;
        let m = matrix[0].length;
        //比较target是否在左上端点和右下端点里
        if (matrix[0][0] <= target && target <= matrix[n - 1][m - 1]) {
    
    
            for (let y = 0; y < n; y++) {
    
    
                //按行遍历
                if (matrix[y][0] <= target && target <= matrix[y][m - 1]) {
    
    
                    for (let x = 0; x < m; x++) {
    
    
                        if (matrix[y][x] != target) {
    
    
                            continue;
                        } else {
    
    
                            return true;
                        }
                    }
                } else {
    
    
                    continue;
                }
            }
            return false;
        } else {
    
    
            return false;
        }
    }
    return 0;
};

解法2: 线性查找.

从右上角开始,如果9>target,说明此列不行,往左查找;找到了<target的值,开始在此列往下查找,如此循环

在这里插入图片描述

/* 时间复杂度O(n+m),空间复杂度O(1) */
var findNumberIn2DArray = function(matrix, target) {
    
    
    if (matrix.length == 0) {
    
    
        return 0;
    }
    let n = matrix.length;
    let m = matrix[0].length;
    //注意这里for循环里两个条件同时满足就不能用逗号隔开,否则代表只有满足其中一个
    for (let x = m - 1, y = 0; y < n && x >= 0;) {
    
    
        if (target == matrix[y][x]) {
    
    
            return true;
        } else if (target < matrix[y][x]) {
    
    
            x--;
        } else if (target > matrix[y][x]) {
    
    
            y++;
        }
    }
    return false;
};
//应该在for循环之前再补一次比较 左上角<=target<=右下角, 懒得改了

运行结果: 解法1和解法2看起来差不太多,应该是测试用例没有特别大的

在这里插入图片描述

二,字符串

1. 替换字符串中的空格-剑指offer05

解法1: String的split()和join()方法实现的

var replaceSpace = function(s) {
    
    
    return s.split(' ').join('%20');
};

并且这种方法对连续的空格同样适用, 检测如下:

s = "We are  happy."//两个空格,那么空格间就是一个空字符串,也会进数组
var replaceSpace = function(s) {
    
    
    var arr = s.split(' ');
    console.log(arr);	//["We", "are", "", "happy."]
    return arr.join('%20');
};
console.log(replaceSpace(s));

解法2: String的replace方法

var replaceSpace = function(s) {
    
    
    return s.replace(/\ /g, "%20");//注意全局匹配
};

三, 链表

1. 从尾到头打印链表-剑指offer06

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
输入:head = [1,3,2]
输出:[2,3,1]

虽然链表用数组形式输入,但head其实是指向链表的头节点的指针

//解法1: 遍历链表压入栈,然后弹出栈就获得了倒序
//时间复杂度O(2N),空间复杂度O(2N)
var reversePrint = function(head) {
    
    
    var arr=[];
    var out=[];
    var t=0;
    while(head !== null){
    
    
        arr.push(head.val);
        head = head.next;
        t++;
    }
    for(var i=0; i<t; i++){
    
    
        out[i]=arr.pop();
    }
    return out;
};

使用链表经常涉及到栈和队列方式操作, 所以这里复习一下如何用数组实现栈和队列:

// 数组的栈操作: push从末端添加任意项, pop从末端弹出最后一项
// 数组的队列操作: 结合push末端添加, 和shift前端弹出, 实现队列
// push,unshift返回添加项后的数组, 它们都可以同时添加多项; pop和shift返回弹出的这一项,每次只能弹出一项
//它们都会修改原数组

2. 删除链表节点-剑指offer18

改变 cur->pre 节点的指针地址等于 cur->next, 即可实现删除

var deleteNode = function(head, val) {
    
    
    if(head==null){
    
    
        return;
    }
    if(head.val == val){
    
    
        return head.next;        
    }
    let ohead = head;
    while(head != null){
    
    
        var p = head;
        head = head.next;
        if(head.val == val){
    
    
            p.next=head.next;
            return ohead;
        }
    }
};

3. 链表中倒数第k个节点-剑指offer22

给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

//遍历一次,但额外开辟一个空间指针数组, 时空复杂度都是O(N)
var getKthFromEnd = function(head, k) {
    
    
    var arr=[]; //装链表的每个节点的指针地址值
    var i=0;
    while(head){
    
    
        arr[i] = head;
        head = head.next;
        i++;
    }
    if(i>=k){
    
    
        return arr[i-k];
    }
    return null;
};

猜你喜欢

转载自blog.csdn.net/Fky_mie/article/details/115940881