在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
function Find(target, array){
//从第一行开始,第一个数比target大的话,
//该行所有数都比target大,跳至下一行
for (let i=0;i<array.length;i++){
if (array[i][0]>target){
continue
}//第一个数比target小的话,该行可能有target
//用indexOf找
else{
if(array[i].indexOf(target)!=-1){
console.log('有这个数!')
return true
}
}
}
return false
}
根据矩阵的特性,从矩阵的左下角看,往右递增往上递减,从左下角开始找,如果比target大则右移如果比target小则左移(注意分清行和列)
function Find(target,array){
var x = array.length; //行
var y = array[0].length; //列
var i = x-1;
var j = 0;
while(i>=0&&j<=y-1){
if (array[i][j]>target){
i--
}
else if(array[i][j]<target){
j++
}
else{
console.log('have')
return true
}
}
console.log('none');
return false
}
js没有多维数组,可以通过数组包含数组创建多维数组
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
for循环遍历整个数组,用indexOf寻找该元素,返回的下标不是该元素的下标说明有重复元素
function duplicate(numbers, duplication){
for(let i = 0; i < numbers.length; i++){
if(numbers.indexOf(numbers[i]) != i){
duplication[0] = numbers[i]
return true
}
}
return false
}
哈希法:用另一个长为n的数组hash,hash的下标值对应numbers中的元素值,而hash的元素值对应numbers元素出现的次数。
若numbers中元素3出现了两次,那么a[3]=2,这样遍历hash数组,哪个元素大于1则下标就是numbers中重复的元素
function duplicate1(numbers, duplication)
{
var length = numbers.length
var hash = new Array(length).fill(0)//fill方法将一个固定值替换数组的元素
for(let i=0;i<length;i++){
hash[numbers[i]]+=1
}
for(let j=0;j<length;j++){
if(hash[j]>1){
duplication[0]=j
return true
}
}
return false
}
进击:上面的做法其实把hash每个元素都赋值了,然又遍历,其实没必要,只要找到hash中第一个第一个不为0的元素就可以了
function duplicate(numbers, duplication)
{
var length = numbers.length
var hash = new Array(length).fill(0)
for(let i=0;i<length;i++){
if(hash[numbers[i]]==0){
hash[numbers[i]]+=1
}else{ //hash[numbers[i]]不为0说明重复出现了
duplication[0]=numbers[i]
return true
}
}
return false
}
数归其位:对于一个数组,把每个元素放到对应值的下标的位置上,这样对于重复的数字,一个下标就有多个数字,思想就是想办法数归其位,当发现某个元素的和值相同下标出的元素相等时,说明有重复数字(wsl)
遍历数组,判断元素a[i]和下标i是否相同
1.若相同跳过()
2.若不同,判断a[i]和a[a[i]]是否相同
2.1若不同,二者交换位置,也就是说a[i]跑到了a[i]位置 上,而a[a[i]]跑到了i位置上,对于元素a[i]来说就归位了
2.2若相同,重复元素出现了
其实元素是通过下标值这个中间量进行比较
function duplicate(numbers, duplication)
{
for(let i=0;i<numbers.length;i++){
while(i!=numbers[i]){
if(numbers[i]==numbers[numbers[i]]){
duplication[0]=numbers[i]
}else{
[numbers[i],numbers[numbers[i]]]=[numbers[numbers[i]],numbers[i]]
}
}
}
return false
}
array.indexOf(item,start)
用于查找array中是否含有item,有则返回 item 的第一次出现的位置否则返回-1
start表示查找开始位置,没有这个参数的话则从数组开头查找
array.fill(value, start, end)
用一个固定值value填充数组,start开始位置,end终止位置,不写默认数组长度
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]**A[1]*…*A[i-1]A[i+1]…A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
根据题目可知,新数组array2的元素array2[i]等于array数组去除元素array[i]后,剩下元素的乘积。
function multiply(array)
{
var length = array.length
var array2 = []
for(let i=0;i<length;i++){
var a = array.splice(i,1) //第一步去掉array[i],返回是一个数组[array[i]]
array2[i] = array.reduce((prevalue,item)=>{
return prevalue*item
},1) //第二步reduce函数计算array剩余元素乘积
array.splice(i,0,a[0])//把去掉的array[i]塞回array,注意a是一个单个元素的数组,所以用a[0]
}
return array2
}
其实新数组的值B[i]可看作一下数组每行的乘积,这个数组分为上下两个三角,每个三角都能连乘。先算下三角中的连乘,即先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。引入一个中间变量tem表示上三角连乘的结果
function multiply(array)
{
var length = array.length
var array2 = []
//下三角计算
array2[0]=1
for(let i=1;i<length;i++){
array2[i]=array2[i-1]*array[i-1]
}
//上三角计算用tem表示
let tem = 1
for(let j=length-2;j>=0;j--){
tem=tem*array[j+1]
array2[j]=array2[j]*tem
}
return array2
}
数组方法1
array.slice(start,end)
从数组下标start处(包括start)开始选取元素,直到下标end处结束(不包括end处),选取的元素包含在返回的新数组中,但是array不改变
字符串也可以用这个方法,返回字符串
var array = [1,2,3,4,5]
var b = array.slice(2,3)
var c = array.slice(-3,-1) //slice(-3+5,-1+5)
console.log(b) //[3]
console.log(c) //[3,4]
console.log(array) //[1,2,3,4,5],不改变原数组
数组方法2
array.splice(index,howmany,item1,...,itemN)
对数组先删除后添加。从下标index处(包括index处)开始删除howmany个元素,然后把item1,…,itemN添加进去。返回包含被删除的元素的数组,原数组改变
array.splice(index,howmany)
没有item的话就是删除元素,返回包含被删除的元素的数组,当howmany为0时,表示没有删除元素,返回的数组为空
var array = [1,2,3,4,5]
var b = array.splice(2,3)
console.log(array) //[1,2],原数组改变了
console.log(b) //[3,4,5],被删元素的数组
var c = array.splice(2,0)
console.log(c) //[],没有删元素,返回空数组
console.log(array) //[1,2]
var d = array.splice(1,1,1)
console.log(d) //[2]
console.log(array); //[1,1]
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
。。。
打印顺序为从左到右,从上到下,从右到左,从下到上。打印的范围也随着一圈打印完而缩小。我们需要知道什么时候改变打印方向而且记录最新的打印范围。
四个变量如下
left = 0
right = matrix[0].length-1,
top = 0
bottom = matrix.length-1
循环打印:从上到下,从右到左,从下到上的顺序打印
开始我的代码这样写的
function printMatrix(matrix) //错错错
{
if(matrix == null) return null
let left = 0,
right = matrix[0].length-1,
top = 0,
bottom = matrix.length-1
let res=[]
while(left<=right&&top<=bottom){ //左右相遇且上下相遇说明没有可打印的元素了
for(let i=left;i<=right;i++){ //left > right
res.push(matrix[top][i])
}
for(let i=top+1;i<=bottom;i++){ //top > bottom
res.push(matrix[i][right])
}
for(let i=right-1;i>=left;i--){
res.push(matrix[bottom][i])
}
for(let i=bottom-1;i>top;i--){ //注意这里不能等于top,否则就会多打印矩阵右上角的元素
res.push(matrix[i][left])
}
//一圈打印完毕,缩小打印范围
left++
right--
top++
bottom--
}
return res
}
这里就出现了一个问题就是:一行/一列的矩阵,在从右往左和从上往下打印时,就会重复打印。
也就是说对于从右往左打印时保证top和bottom不重合,从上往下打印时保证left和right不重合。修改如下
function printMatrix(matrix)
{
if(matrix == null) return null
let left = 0,
right = matrix[0].length-1,
top = 0,
bottom = matrix.length-1
let res=[]
while(left<=right&&top<=bottom){
for(let i=left;i<=right;i++){
res.push(matrix[top][i])
}
for(let i=top+1;i<=bottom;i++){
res.push(matrix[i][right])
}
for(let i=right-1;i>=left&&top<bottom;i--){
res.push(matrix[bottom][i])
}
for(let i=bottom-1;i>top&&left<right;i--){ //注意这里不能等于top,否则就会多打印矩阵右上角的元素
res.push(matrix[i][left])
}
left++
right--
top++
bottom--
}
return res
}
这道题和第二题有点类似。我的思路是弄个哈希数组hash其中元素表示数组某个元素的出现次数,hash的下标值对应数组元素,从头到尾遍历数组,第一次出现就push0,否则+1。但是有个局限:数组元素不能为负。
把数组从小到大排序,重复出现的数字肯定是连着的,如果他的次数大于数组长度一半,那中位数肯定是他。
采用快速排序,或者偷懒用数组的sort()方法也可以。
function MoreThanHalfNum_Solution(numbers)
{
let hash = []
numbers.sort((a,b)=>{
return a - b
})
let mid = numbers[Math.floor(numbers.length/2)]
let count = 0
for(let item of numbers){
if(item == mid)
count++
}
return count>Math.floor(numbers.length/2)? mid:0
}
如果存在这种数字的话,那么数组其他数字次数加起来也没有这个数字次数多。
在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所得。然后再判断它是否符合条件即可。
这是一种两两不同抵消的思想,最坏的情况,这个数字都被抵消了(如[1,2,1,2,1]),由于这个数字大于数组长度一半,他肯定还剩一个。
function MoreThanHalfNum_Solution(numbers)
{
let tmp = numbers[0]
let num = 1
for(let item of numbers){
if(item == tmp){
num++
}else{
num--
}
if(num == 0){
tmp = item
num = 1
}
}
//统计次数
let count = 0
for(let item of numbers){
if(item == tmp)
count++
}
return count>Math.floor(numbers.length/2)? tmp:0
}
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
暴力解题
function GetLeastNumbers_Solution(input, k)
{
if(k>input.length) return []
input.sort((a,b)=>{
return a - b
})
return input.slice(0,k)
}
看了题解多是用快排,躲不掉。其实这里利用了快排的partition函数,并没有完全对数组排序,而是找到第k小的数所在的部分,比如在右半部分,因为左半部分是比pivot小的,而k大于pivot的index,那么只要对右半部分排序就可以了,取出部分(可能左半部分不够k个数)。
function GetLeastNumbers_Solution(input, k)
{
if (input.length == 0 || k > input.length || k < 1) return []
let index = Partition(input,0,input.length-1)
//判断k的位置,这样知道在哪边找
while(index!=k-1){
if(index>k-1){
index = Partition(input,0,index-1)
}else{
index = Partition(input,index+1,input.length-1)
}
}
let res = input.slice(0,k)
res.sort((a,b)=>{return a-b}) //排序
return res
}
//partition函数很重要
function Partition(arr,left,right){
let pivot = arr[left] //比较的基准
//一直找到比基准小的数
while(left<right){
while(pivot<=arr[right]&&left<right){
right --
}
//放在前面
[arr[left],arr[right]] = [arr[right],arr[left]]
//找到比基准大的数
while(pivot>=arr[left]&&left<right){
left ++
}
//放在后面
[arr[left],arr[right]] = [arr[right],arr[left]]
//两个循环两个交换,保证左边都是比基准小的数,右边都是大的数,left为基准的下标
return left
}
}
快排代码
function quickSort(a,left,right){
if(left==right)return;
let index=partition(a,left,right);//选出key下标
if(left<index){
quickSort(a,left,index-1);//对key的左半部分排序
}
if(index<right){
quickSort(a,index+1,right)//对key的右半部份排序
}
return a
}
function partition(a,left,right){
let pivot=a[left];//让基准为第一个数
while(left<right){
//找到比pivot小的数
while(pivot<=a[right]&&left<right){
right--;
}
//交换给前面,这样保证前面都是比基准小的数
[a[left],a[right]]=[a[right],a[left]]
//找到比pivot大的数
while(pivot>=a[left]&&left<right){
left++;
}
//交换给后面,这样后面都是比基准大的数
[a[left],a[right]]=[a[right],a[left]];//交换
}
//left和right相遇或者left超过right,返回pivot的下标
return left
}
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1
反正我是被忽悠住了。
动态规划:多用来求最值问题。
动态规划方法1
力扣上这个解析很清晰连续子数组的最大和(动态规划,清晰图解)
function FindGreatestSumOfSubArray(array)
{
let res = array[0]
let dp = array[0]
for(let i=1;i<array.length;i++){
if(dp>0){
dp = dp+array[i]
}else{
dp = array[i]
}
res = Math.max(dp,res)
}
return res
}
动态规划方法2
function FindGreatestSumOfSubArray(array)
{
let res = array[0]
let dp = array[0]
for(let i=1;i<array.length;i++){
dp = Math.max(dp+array[i],array[i])
res = Math.max(dp,res)
}
return res
}
个人觉得第二种方法更好理解。
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
自暴自弃法,所有可能的组合都列出来,把最小的输出。这个真的不太现实,组合列出来的话就是之前abc字符串排列组合那道题,就真的很麻烦。
定义新的排序规则,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。
function PrintMinNumber(numbers)
{
numbers.sort((s1,s2)=>{ //s1表示后一个数,s2表示前一个数
let a = `${s1}${s2}`
let b = `${s2}${s1}`
return a-b //return正值时,s1和s2的位置不变也就是说s1在s2的后面
})
let s = ''
for(let item of numbers){
s+=item
}
return s
}
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
??????
归并算法。算法原理可以参考这片文章图解归并算法,其中这张图片很好的说明了归并算法的原理
这道题就在治的过程中统计逆序对的数目。以[7,5,6,4]为例。
首先归并排序的分
arr分解为L= [7,5],R = [6,4];继续分解为LL = [7], LR = [5];和RL = [6], RR = [4];
自此分解完成。
接下来合并
i为arrLL的数组下标,j为arrLR的数组下标, index为新数组res的下标,初始值都为0
function InversePairs(data)
{
var sum = 0
MergeSort(data,sum)
return sum%10000000007
//下面都是归并排序
function MergeSort(arr){
if(arr.length < 2){
return arr
}
let len = arr.length
let mid = Math.floor(len/2)
let left = arr.slice(0,mid)
let right = arr.slice(mid)
return Merge(MergeSort(left),MergeSort(right))
}
function Merge(left,right){
let res = []
let i = 0,j = 0
let leftLen = left.length
let rightLen = right.length
while(i<leftLen&&j<rightLen){
if(left[i]<right[j]){
res.push(left[i])
i++
}else{
res.push(right[j])
j++
sum += leftLen - i //与归并排序的唯一区别
}
}
while(i<leftLen){
res.push(left[i])
i++
}
while(j<rightLen){
res.push(right[j])
j++
}
return res
}
}
归并排序代码
function MergeSort(arr){
if(arr.length < 2){ //递归出口
return arr
}
let len = arr.length
let mid = Math.floor(len/2)
//左右划分
let left = arr.slice(0,mid)
let right = arr.slice(mid)
return Merge(MergeSort(left),MergeSort(right))
}
function Merge(left,right){
let res = []
let i = 0,j = 0
let leftLen = left.length
let rightLen = right.length
while(i<leftLen&&j<rightLen){
if(left[i]<right[j]){
res.push(left[i])
i++
}else{
res.push(right[j])
j++
}
}
//可能右数组比较完了,左数组还有多的
while(i<leftLen){
res.push(left[i])
i++
}
while(j<rightLen){
res.push(right[j])
j++
}
return res
}
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
用indexOf和lastIndexOf,二者返回值相等说明找到了只出现一次的数字了。一个从前往后找一个从后往前找。
function FindNumsAppearOnce(array)
{
let res = []
for(let i=0;i<array.length;i++){
if(array.indexOf(array[i])==array.lastIndexOf(array[i]))
res.push(array[i])
}
return res
}
注意数组的特点:其他数字出现两次。异或运算有个特点:两个相同数字异或=0,一个数和0异或还是它本身。
把数组元素依次异或,最后的结果肯定就是出现一次的两个数字的异或结果,其他数字是成对的,异或都等于0了。
然后要从结果分离出两个数字:这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
function FindNumsAppearOnce(array)
{
let tmp = array[0]
//数组异或
for(let i=1;i<array.length;i++){
tmp = tmp ^ array[i]
}
if(tmp == 0) return
//原数组分离
let index = 0
while((tmp&1) == 0){
tmp = tmp >>> 1
index ++
}
let num1=0,num2=0
for(let i=0;i<array.length;i++){
let flag = isOneOfIndex(array[i],index)
if(flag){ //flag是多少都不重要,这里就是根据index上是不是1把array中元素分开
num1 = num1^array[i]
}else{
num2 = num2^array[i]
}
}
return [num2,num1]
}
//判断num的index位上是不是1
function isOneOfIndex(num,index){
num = num>>>index
return num&1 //是1返回1就是true
}
统计一个数字在排序数组中出现的次数
如果这个数字重复的话,由于是排序数组,肯定是连在一起。找到之后,判断下一个数是否相等,不等就返回1,相等就边计数边遍历直到与这个数不相等。
function GetNumberOfK(data, k)
{
let i=0
while(data[i]!=k){
//数字k不存在的情况
if(i==data.length){
return 0
}
i++
}
//下一个不相等或者最后一位是数字k
if(data[i+1]!==k || i==data.length-1)
return 1
let count = 0
while(data[i]==k){
count++
i++
}
return count
}
更多采用的办法是二分查找。边界处理是真的头疼,看了好久。力扣的这篇题解中,有很直观的动画,以供参考。在排序数组中查找数字
我们要通过二分法来找数组的两个边界。
function GetNumberOfK(data, k)
{
//let[i,j] = [0,data.length-1]
let i = 0
let j = data.length-1
while(i<=j){
let mid = Math.floor((i+j)/2)
if(data[mid]<k){
i = mid+1
}else{
j = mid-1
}
}
let left = j
i = 0
j = data.length-1
while(i<=j){
let mid = Math.floor((i+j)/2)
if(data[mid]<=k){
i = mid+1
}else{
j = mid-1
}
}
let right = i
return right-left-1
}
二分查找
二分法用于有序数据的查找。
算法描述
function BinarySearch(arr,k){
let low = 0
let high = arr.length-1
while(low<=high){ //注意点:结束条件
let mid = Math.floor((low+high)/2) //注意点:下取整
if(arr[mid]<k) {low = mid+1}
if(arr[mid]>k) {high = mid-1}
if(arr[mid]==k) return mid
}
return low //这里return是指找不到时,k应该插入的位置
}