4、数据结构与算法

文章目录

好未来数据结构与算法题

1、快排的时间复杂度是多少?怎么计算的?空间复杂度是多少?怎么计算的?手写冒泡和快排?

时间复杂度:指的是算法完成需要的时间
空间复杂度:指的是算法完成需要的存储空间
冒泡排序的时间复杂度是O(n^2); 空间复杂度是O(1)
快速排序的时间复杂度是O(nlg(n));空间复杂度是O(n),这里的n相当于压栈的次数
快速排序的时间复杂度计算
快速排序采用的是分区排序的方式,假设数据个数为n,每次分区的比例为1:9,那么每次遍历的数据个数均为n。用递归树表示,现在要求的就是递归树的高度
在这里插入图片描述
因为对数的复杂度,不管底数是多少,均为底数为10,所以递归树深度为 lg(n),再乘以每层需要遍历的数据个数,所以为O(n
lg(n))。
1、冒泡排序
原理:
从第一个元素开始,相邻两个元素亮亮进行比较,较大者往后放,这样第一轮下来,就把最大的数放在了最后边;然后表示第二个元素的两两比较,还是较大者往后放,这样下来,倒数第二大的元素就被放在了倒数第二个位置上。
时间复杂度
比较次数:n-1 + n-2 + …+1 = n(n-1)/2,所以时间复杂度是O(n^2)
代码实现

 function bubbleSort(arr) {
   
            var length = arr.length
            for(var i = 0; i < length - 1; i++) {
   
                for(var j = 0; j < length - 1 - i; j++) {
   
                    if(arr[j] > arr[j+1]) {
   
                        var temp = arr[j]
                        arr[j] = arr[j+1]
                        arr[j+1] = temp
                    }
                }
            }
            return arr
        }
console.log(bubbleSort([7,6,5,4,3,2,1,8])); //[1,2,3,4,5,6,7,8]

2、快速排序

<script>
       //快速排序 
       //1、找枢纽 用中位数 首先用索引值为 left right (left + right) / 2 进行排序,结束之后,最大的数,就放在最后边,然后把arr[center] 和 arr[right - 1] 交换位置,第二大的数也就是枢纽,被放在了arr[right - 1]的位置
       //2、之后给两个指针,左指针从left开始,找比pivot大的数;右指针从right开始,找比枢纽小的数,两个都找到的话,就交换arr[i]和arr[j];当left = right的时候,就把arr[i]和arr[right](也就是枢纽所在的位置交换)最后返回该枢纽pivot
       //3、对给定的数组,left和right进行快排,先把枢纽找到并放入正确的位置,然后在对枢纽左边的和右边的进行递归快排
       var arr = [85,8,88,77,77,84,74,7,74,4,2,5,85,97,48]
       console.log(quickSort(arr, 0, arr.length - 1))


       function Pivot(arr,left,right) {
   
           var center = Math.floor((left + right) / 2)
           if(arr[left] > arr[center]) {
   
               [arr[left], arr[center]] = [arr[center], arr[left]]
           }
           if(arr[center] > arr[right]) {
   
               [arr[center], arr[right]] = [arr[right], arr[center]]
           }
           if(arr[left] > arr[right]) {
   
               [arr[left], arr[right]] = [arr[right], arr[left]]
           }
           [arr[center],arr[right - 1]] = [arr[right - 1], arr[center]]
           return arr[right - 1]
       }
       function pivotChange(arr, left, right) {
   
           var i = left
           var j = right - 2
           var pivot = Pivot(arr,left,right) //找到枢纽值
           
            while(i < j) {
   
                while(i < j && arr[j] >= pivot) {
   
                   j--
                }
                while(i < j && arr[i] <= pivot) {
   
                   i++
                }
                if(i !== j) {
   
                    [arr[i],arr[j]] = [arr[j], arr[i]]
                }
            }
            //这里注意 应该是 交换 i + 1和原先的枢纽值
           [arr[i + 1], arr[right - 1]] = [arr[right - 1], arr[i + 1]]
           return i + 1
       }
       function quickSort(arr, left, right) {
   
           if(left < right) {
   
              var i =  pivotChange(arr, left, right)
              quickSort(arr, left, i - 1)
              quickSort(arr, i + 1, right)
           }
           return arr
       }
</script>

2、常见的数据结构和应用场景

1、栈:先进后出,比如课本的发放。
2、队列:先进先出,比如排队看电影
3、链表:用于存储数据,每个数据存储在一个节点中,该节点包括数据值和next(用于指向下一个节点)。
链表和数组的区别:当该组数据需要改/查的时候用数组,当该组数据需要增/删的时候用链表。数组定义前必须指明数组长度并且是开辟连续的内存空间。链表是从前往后找。
双向链表:有prev item next组成,pre指向前一个节点,next指向下一个节点,item用于存放本身的 数据。
4、哈希表:解决了链表和数组存在的问题,对元素的增删改查极快,但是哈希表的数据是无序的。用于公司员工信息的存储。
5、树:生活实例,比如家庭成员图。因为哈希表是无序排放的,不能按照固定的顺序去访问一组数据,所以用树来解决,树的缺点是效率没有哈希表高。经常使用的是二叉搜索树,节点的左子树小于节点,节点的右子树大于节点,并且左右子树也是二叉搜索树。
项目中用到的算法:
快速排序:从服务器请求商品的价格信息,然后把价格按照由低到高排序,从时间复杂度考虑,我们选用快速排序,从空间复杂度考虑,选用冒泡排序。

3、合并两个有序数组并进行排序

题目:给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
思路:给两个指针p1 p2分别指向 nums1和nums2,然后比较nums[p1]和nums[p2]的大小,把小的追加到新数组中,然后让这个p在加 1 传给下一次的比较,结束的条件是p1<m或者是 p2 < n;然后遍历新数组,把新数组的元素赋值给nums1。

var merge = function(nums1, m, nums2, n) {
   
    var p1 = 0
    var p2 = 0
    var sorted = []
    var cur
    while(p1 < m || p2 < n ) {
   
        if(p1 == m) {
   
            cur = nums2[p2++] //后置递增,先返回原值在加一,在这里就是cur = nums[p2] p2+=1在把这个p2传给下一次的比较。
        } else if(p2 == n) {
   
            cur = nums1[p1++]
        } else if(nums1[p1] < nums2[p2]) {
   
            cur = nums1[p1++]
        } else {
   
            cur = nums2[p2++]
        }
        sorted[p1 + p2 - 1] = cur //这里要注意的的是数组的索引
    }
    for(var i = 0; i !== m + n; i++) {
   
        nums1[i] = sorted[i]
    }
    return nums1
};

4、二维数组相加相乘怎么计算?

二维数组的相加

var array = [["1.2","1.3","1.5","1.7"],["2.1","2.3","2.4"]]
var sum = []
for(var i = 0; i < array.length; i++) {
   
           sum[i] = 0
}
for(var count1 = 0; count1 < array.length; count1++) {
   
     for(var count2 = 0; count2 < array[count1].length; count2++) {
   
          sum[count1] += parseFloat(array[count1][count2])
	}
}
console.log(sum);

6、求前十个最大的值

//求数组中前十个最大的值
       //思路:首先对数组进行排序后反转数组,之后把数组的前十个元素输出
       function Sort(arr, n) {
   
            arr = arr.sort((a,b) => a-b).reverse()
            var newArr = []
            for(var i = 0; i < n; i++) {
   
                newArr.push(arr[i]) 
            }
            return newArr
}
console.log(Sort([1,2,3,43,4,5,6,7,8,98,6,45,3,3,5,6,7,2,12,6,8,3], 10));

8、奖英文字符串按照大小写反转,并且以空格进行逆序

题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”

var reverseWords = function(s) {
   
    //1.首先把字符串两端的空格去掉 然后把字符串以空格为间隔分隔成数组 再把数组中的空格过滤掉
    s = s.trim().split(" ").filter(value => value != '')
    //2.对数组元素实现reverse
     s = s.reverse()
    //3.拼接字符串 以空格拼接成一个数组
    return s.join(" ")
};

9、100个人循环报数,报到5的时候就淘汰,最后剩下一个人

function baoshu(n,m) {
   
           var arr = []
           for(var i = 1; i <= n; i++) {
   
               arr.push(i)
           }

           while(arr.length > 1) {
   
               for(var j = 0; j <= m; j++) {
   
                   arr.push(arr.shift())
               }
               arr.shift()
           }
           return arr
}
console.log(baoshu(100,5));

10、reduce求和

//reduce方法 加数组的扁平化的实现
       var arr = [1,2,3,[4,5],6]
       var sum = arr.toString().split(",").reduce((total,i) => total += Number(i), 0)
       console.log(sum);

11、实现数组的乱序输出

function mixed(arr) {
   
           for(var i = 0; i < arr.length; i++) {
   
               var random = Math.round(Math.random() * (arr.length - 1 - i)) + i;
               [arr[i], arr[random]] = [arr[random], arr[i]]
           }
           return arr
}
console.log(mixed([1,2,3,4,5]));

12、将出现次数最多的前k 个单词输出

给一非空的单词列表,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。
题解:

var topKFrequent = function(words, k) {
   
   //思路涉及到存储单词和出现的频率问题 考虑用字典实现
   //1、新建字典 用于保存单词和出现次数
   var ctn = new Map()
   //2、把单词和出现次数放入 字典
   for(var word of words) {
   
       ctn.set(word, (ctn.get(word) || 0) +1)
   }
   //3、把单词存储在 一个数组中
   var res = []
   for(var entry of ctn.keys()) {
   
       res.push(entry)
   }
   //4、对res中的单词按照出现次序进行排序
   res.sort((word1,word2) => {
   
       return ctn.get(word1) === ctn.get(word2) ? word1.localeCompare(word2) : ctn.get(word2) - ctn.get(word1)
   })
   //输出前 k  个单词构成的数组
return res.slice(0, k)
};

13、匹配括号

题目:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
思路:碰到左括号的时候就入栈,碰到有括号的时候就判断是不是和栈顶元素匹配,如果匹配的话就删除该元素,不匹配的话 返回 false
最后返回栈的元素个数即栈的长度为 0
题解:

var isValid = function(s) {
   
    //字符串s的长度为奇数时,直接返回false
    if(s.length%2 ==1) return false ;
    //通过数组建立栈
    let stack = [];
    for(let i = 0;i<s.length;i++){
   

        let c = s[i];
        console.log(c)
        //如果为左括号就进栈
        if(c==='{' || c==='[' || c==='(') {
   
          stack.push(c);
        } else {
   

            //若为右括号,若栈空时,返回一个false;
            if(stack.length===0) return false ;
            //取到栈顶元素
            let s = stack[stack.length-1];
            //判断是否匹配,ruo匹配就出栈顶元素,可以不用接收返回结果
            if(s==='{'&&c==='}' || s==='['&&c===']' || s=='('&&c==')') {
   
                stack.pop();
            } else {
   
                return false ;
            }
        }
    }
    //遍历完所有字符串后,判断栈是否为空,若为空,即完全配对
    return stack.length ===0;
};

<一>、基本算法题

1、双指针—和为s的两个数字

题目:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

思路1:用循环,第一个加数依次为数组顺序元素,第二个加数从第一个加数往后延,直至找到和为target的两个加数。

题解:

var twoSum = function(nums, target) {
   
    for(var i = 0; i < nums.length; i++) {
   
        for(var j = i + 1; j < nums.length; j++) {
   
            if(nums[i] + nums[j] == target) return [nums[i], nums[j]]
        }
    }
};

思路2:因为数组元素是递增的,所以使用双指针来遍历。左右均给定一个指针,当左右指针的元素之和等于target时,输出这两个指针的值;当小于target时,左指针右移;当大于target时,右指针左移。

题解:

var twoSum = function(nums, target) {
   
    var l = 0
    var r = nums.length - 1
    while(l < r) {
   
        if(nums[l] + nums[r] == target) return [nums[l], nums[r]]
        else if(nums[l] + nums[r] < target) l += 1
        else  r -= 1
    }
};

2、和为s的连续正数序列

题目:
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
思路:给定两个指针, 一个sum,和一个用于存放结果的数组。当和小于目标值时,和加上这个数,右指针往右;当和大于目标值的时候和放弃这个数,然后左指针往右;当左右指针之间的和等于目标值的时候把元素追加到一个数组中;遍历完毕之后,让和减去左指针的值,左指针右移,进行下一次遍历直至左指针到中间。然后把每次遍历的数组放到一个数组中。

var findContinuousSequence = function(target) {
   
    var list = []
    var left = 1
    var right = 1
    var sum = 0
    while(left < target/2) {
   
        if(sum < target) {
   
            sum += right
            right += 1
        } else if(sum > target) {
   
            sum -= left
            left += 1
        } else {
   
            var arr = []
            for(var i = left; i < right; i++) {
   
                arr.push(i)
            }
            list.push(arr)
            sum -= left
            left += 1
        }
    }
    return list
};

3、二进制中1的个数

题目:请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。

思路:先把十进制转换成二进制,用数组实现。把余数放入一个数组中,十进制变成整数商;除完之后,把数组中的余数从后往前放入一个新数组,构成该十进制的二进制数;最后遍历这个二进制数组,用index计数。

题解:

var hammingWeight = function(n) {
   
    var arr = []
    while(n > 0) {
   
        arr.push(n % 2)
        n = Math.floor(n / 2)
    }

    var a = []
    while(arr.length) {
   
        a.push(arr.pop())
    }

    var index = 0
    for(var i = 0; i < a.length; i++) {
   
        if(a[i] == 1) {
   
            index += 1
        }  
    }
    return index
}

4、不用加减乘除做加法

题目:写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

思路:清晰加法的本质,不进位加法是 a^b;进位是(a&b)<<1;如果进位不为0,就一直把上次得到的不进位和进位进行循环,直至进位为0。

题解:

var add = function(a, b) {
   
    while(b) {
   
        var sum = a^b
        b = (a&b)<<1
        a = sum
    }
    return a
};

5、实现1+2+…+n不用乘除法

题目:
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3
输出: 6
思路1:采用递归

var sumNums = function(n) {
   
    return n && sumNums(n - 1) + n
};

思路2:运用对数。可以将乘法转换成加法,因为这道题就是求等差数列的和 公式为 n * (1 + n) / 2;所以想办法把乘法转换成加法

var sumNums = function(n) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值