本文属于系列文章《你真的了解二叉树吗》的第二部分——手撕算法篇。如果你还没有看过第一部分[《你真的了解二叉树吗(树形结构基础篇)》] 的话,强烈建议先看一下第一部分的内容,这样你在解题时会更加如虎添翼。很多第一篇里面已经讲过的内容,在这里将不再赘述。
ydtech
如果你有看过上一篇文章[《你真的了解二叉树吗(树形结构基础篇)》] 的话,应该已经知道了,我们树的遍历天生就适合使用递归实现。
此外,还讲了如何设计和实现一个递归函数,如果对这两点不太了解或没看过上一篇文章的同学,请先移步[《你真的了解二叉树吗(树形结构基础篇)》] 补一下基础,这里就不赘述了,直接讲思路吧。
首先,先设计一下我们的递归函数
/*
* @lc app=leetcode.cn id=144 lang=typescript
*
* [144] 二叉树的前序遍历
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function preorderTraversal(root: TreeNode | null, arr: number[]=[]): number[] {
// 判断边界条件
if(root) {
// 因为是前序遍历,所以先将根节点加入数组
arr.push(root.val);
// 递归遍历左子树和右子树
preorderTraversal(root.left, arr);
preorderTraversal(root.right, arr);
}
return arr;
};
// @lc code=end
以上是常规的递归方式实现,我们也可以使用迭代的方式实现:
/**
* 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 preorderTraversal = function(root) {
// 用一个栈辅助完成迭代遍历
const stack = [];
// 结果数组
const res = [];
// 当前节点
let cur = root;
// 如果当前节点为空或者栈为空时结束循环
while(cur || stack.length>0) {
while(cur) {
// 因为是前序遍历,所以根节点先存放到结果数组
res.push(cur.val);
// 为了在我们走到左子树的底部时,能够回到根节点继续遍历右子树,因此,先将根节点存入栈中
stack.push(cur);
// 继续遍历左子树,直到遍历到叶子节点
cur = cur.left;
}
// 左子树遍历完了,我们需要遍历右子树了,首先我们先要从栈顶把最近的一个根节点弹出
cur = stack.pop();
// 遍历弹出的这个根节点的右子树
cur = cur.right;
}
return res;
};
解题思路其实跟二叉树的解题思路一模一样,这里就不再赘述了。
首先,先设计一下我们的递归函数
/*
* @lc app=leetcode.cn id=589 lang=typescript
*
* [589] N 叉树的前序遍历
*/
// @lc code=start
/**
* Definition for node.
* class Node {
* val: number
* children: Node[]
* constructor(val?: number) {
* this.val = (val===undefined ? 0 : val)
* this.children = []
* }
* }
*/
function preorder(root: Node | null, arr: number[] = []): number[] {
// 边界条件
if(!root) return arr;
// 前序遍历,先将根节点放入结果数组
arr.push(root.val);
// 循环递归调用所有的子节点
root.children.forEach(child => {
preorder(child, arr);
});
return arr;
};
// @lc code=end
我们要反转一个二叉树,就要先将这个二叉树的左子树的子树和右子树的子树先反转,然后再反转根节点的左右子树。这里利用到了 js 中的解构赋值的新特性进行高效快速反转。
首先,先设计一下我们的递归函数
/*
* @lc app=leetcode.cn id=226 lang=typescript
*
* [226] 翻转二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function invertTree(root: TreeNode | null): TreeNode | null {
// 边界条件,当root为空时直接返回
if(!root) return root;
// 先交换root的左右子树,这里我们利用js的解构赋值的新特性可以迅速的交换左右子树
[root.left, root.right] = [root.right, root.left];
// 分别递归左右子树
invertTree(root.left)
invertTree(root.right)
return root;
};
// @lc code=end
这道题其实就是我们二叉树的层序遍历,将每一层的节点依次输出。这里可以使用一个技巧,增加一个变量k代表当前遍历到二叉树的第几层,默认为0,即默认是二叉树的第一层
首先,先设计一下我们的递归函数
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function levelOrder(root: TreeNode | null,k=0, res: number[][] = []): number[][] {
// 边界条件,当root不存在时,直接返回结果数组
if(!root) return res;
// 如果结果数组的长度为k,说明我们准备遍历新的一层,但这层还没有数组来存放数据,所以要新建一个数组放到结果数组末尾
if(res.length===k) res.push([]);
// 将根节点的值放到第k位的数组中
res[k].push(root.val);
// 递归遍历左子树、右子树
levelOrder(root.left, k+1, res);
levelOrder(root.right, k+1, res);
return res;
};
我们二叉树层序遍历的实现不仅可以使用递归,使用队列+迭代的方式也可以实现,思路也都差不多。
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function levelOrder(root: TreeNode | null, res: number[][]=[]): number[][] {
// 边界条件,root不存在是直接返回空数组
if(!root) return res;
// 用一个队列来存储节点,初始时根节点入队
const queue: TreeNode[] = [root];
// 用于记录当前遍历到了第几层
let k = 0;
while(queue.length!==0) {
// 当层数与结果数组长度一致时,说明遍历到了下一层,但没有一个数组用来存放下一层的数据,因此在结果数组最后放一个空数组
if(k===res.length) res.push([]);
// 将队列中的所有节点依次弹出,此时,队列中的所有节点都是当前层的节点,并且是从左到右排列的
let queueLen = queue.length;
// 注意,我们这行的终止条件是针对于本次循环的队列长度,如果在循环过程中动态加入队列中的元素,是不会循环到的,因此,可以在循环过程中
// 不断的将有子节点的元素的子节点压入队列而不用担心把下一层的数据也在本行输出
while(queueLen--) {
// 当根节点出队并将结果压入到第k层的数组中
const item = queue.shift();
res[k].push(item.val);
// 如果左子树存在则左子树入队
item.left && queue.push(item.left);
// 右子树存在,则右子树入队
item.right && queue.push(item.right);
}
// 本层所有节点处理完后,记得层数加一,准备遍历下一层
k++;
}
return res;
};
这道题其实跟上一道题没什么区别,仅仅是将上一道题的结果数组反转,这个在 js 中实现极其简单,就不再赘述了。
/*
* @lc app=leetcode.cn id=107 lang=typescript
*
* [107] 二叉树的层序遍历 II
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function _lavelOrder(root: TreeNode|null, k: number, arr: number[][] = []): number[][] {
if(!root) return arr;
if(arr.length===k) arr.push([]);
arr[k].push(root.val);
_lavelOrder(root.left, k+1, arr);
_lavelOrder(root.right, k+1, arr);
return arr;
}
function levelOrderBottom(root: TreeNode | null): number[][] {
const res = _lavelOrder(root, 0);
// 使用双指针方式交换反转数组
for(let i=0,j=res.length-1;i<j;i++,j--) {
[res[i], res[j]] = [res[j], res[i]];
}
return res;
// 也可以直接使用数组自带的反转方法
// return res.reverse();
};
// @lc code=end
这一题其实是上面两题思路的综合,我们只需要先按照正常的层序遍历输出得到结果数组,然后将结果数组中的奇数行进行翻转就可以了。
当然,我们可以用更好的方法,就是在进行层序遍历时,直接判断当前的层数k是奇数还是偶数,如果是奇数就往数组前面添加元素,否则往数组后面添加元素,这样就不需要额外再反转数组了。
方案一:先正常层序遍历,然后将奇数行数组反转
function _zigzagLevelOrder(root: TreeNode | null, k:number=0, res: number[][]=[]): number[][] {
if(!root) return res;
if(k===res.length) res.push([]);
res[k].push(root.val);
_zigzagLevelOrder(root.left, k + 1, res);
_zigzagLevelOrder(root.right, k + 1, res);
return res;
};
function zigzagLevelOrder(root: TreeNode | null): number[][] {
const res = _zigzagLevelOrder(root);
// 将奇数行反转
return res.map((item, index) => {
if(index&1) {
item = item.reverse();
}
return item;
})
}
方案二:在进行层序遍历时,如果k是奇数,则从数组前面开始添加元素,否则从后面添加元素。
function _zigzagLevelOrder(root: TreeNode | null, k:number=0, res: number[][]=[]): number[][] {
if(!root) return res;
if(k===res.length) res.push([]);
(k&1)?res[k].unshift(root.val):res[k].push(root.val);
_zigzagLevelOrder(root.left, k + 1, res);
_zigzagLevelOrder(root.right, k + 1, res);
return res;
};
function zigzagLevelOrder(root: TreeNode | null): number[][] {
return _zigzagLevelOrder(root);
}
ydtech
概念:对于任意一个子树而言,他的左右子树的高度差的绝对值不超过 1 的树被称为平衡二叉树。
那么,依照题意,我们能想到的就是计算一棵树的左右子树的树高,然后看看他们绝对值差是否大于1。
为了仅使用一次递归就判断出这棵树是否是平衡树,我们可以在计算树高的时候,假设如果这棵树不是平衡树,也就是说左右子树树高差的绝对值是大于1的时候,我们就直接返回一个负数,如-1。这样,当我们的程序执行完后,只要判断树高是否小于0就可以知道当前的树是否是平衡树了。
首先,先设计一下我们的递归函数
/*
* @lc app=leetcode.cn id=110 lang=typescript
*
* [110] 平衡二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function getHeight(root: TreeNode | null): number {
// 边界条件,如果根节点为空,则树高0
if(!root) return 0;
// 分别计算左右子树的树高
const lH = getHeight(root.left);
const rH = getHeight(root.right);
// 假设:如果当前root的左右子树不平衡,则返回一个负数,如-1
// 除此之外,因为是递归程序,如果左右子树的子树的高小于0,那就说明他们的子树已经不平衡了,无需继续递归,直接返回-1
if(Math.abs(lH-rH)>1 || lH < 0 || rH < 0) return -1;
// 一个树的树高,应该是左右子树的树高最大值加一
return Math.max(lH, rH) + 1;
}
function isBalanced(root: TreeNode | null): boolean {
// 只要树高大于等于0,说明这颗树是平衡的
return getHeight(root) >= 0;
};
// @lc code=end
这道题的解题思路其实就是在我们不断往下找的时候,都把 targetNum 与当前的节点的值计算出一个差值,作为下一次递归时的 targetNum,这样,当我们找到叶子节点的时候,只要叶子节点端的值等于 targetNum 的话,就说明存在这条路径了。
首先,先设计一下我们的递归函数
/*
* @lc app=leetcode.cn id=112 lang=typescript
*
* [112] 路径总和
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function hasPathSum(root: TreeNode | null, targetSum: number): boolean {
// 边界条件,如果root为空时,直接返回false
if(!root) return false;
// 如果当前节点是叶子节点,那么这个节点的值必须等于targetSum
if(!root.left && !root.right) return root.val === targetSum;
// 在左子树中查找是否有满足条件的路径
if(root.left && hasPathSum(root.left, targetSum - root.val)) return true;
// 在右子树中查找是否有满足条件的路径
if(root.right && hasPathSum(root.right, targetSum - root.val)) return true;
// 都不满足则返回false
return false;
};
// @lc code=end
如果有看过我写的上一篇文章[《你真的了解二叉树吗(树形结构基础篇)》] 的同学应该是不会陌生了,上一篇文章中,已经带着大家在思维逻辑层面上分析过这题的解题思路了,但是没有一起编码实现,现在来还债了。
再来回顾一下大概的思路:
# 前序遍历输出结果
<1> 5 2 3 4# 中序遍历输出结果
5 <1> 3 2 4
# 我们知道前序遍历的第一位就是我们二叉树的根节点,那么,我们根据这个根节点的数值在中序遍历中找到1所在的位置
# 这样,我们就知道,5是左子树,3 2 4是1的右子树序列,左子树无需继续处理,右子树可以直接递归处理得到最终的结果。
/*
* @lc app=leetcode.cn id=105 lang=typescript
*
* [105] 从前序与中序遍历序列构造二叉树
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
if(preorder.length===0) return null;
// 用于记录根节点在中序遍历中的位置
let pos = 0;
// 找到根节点在中序遍历序列中的位置
while(inorder[pos] !== preorder[0]) ++pos;
// 分别用来存储拆分后的左右子树的前序和中序遍历序列
let l_pre = [], l_in = [], r_pre=[],r_in=[];
// 将左子树的前序遍历序列和中序遍历序列存起来
for(let i=0;i<pos;i++) {
l_pre.push(preorder[i+1]);
l_in.push(inorder[i]);
}
// 将右子树的前序遍历和中序遍历的序列存起来
for(let i=pos+1;i<preorder.length;i++) {
r_pre.push(preorder[i]);
r_in.push(inorder[i]);
}
// 构建二叉树
const node = new TreeNode(preorder[0], buildTree(l_pre, l_in), buildTree(r_pre, r_in));
return node;
};
// @lc code=end
这道题就是让我们算一下一棵树有几个节点,我们只要知道这样的一个公式:总结点数量=左子树节点数量+右子树节点数量+根节点数量(1)即可
/*
* @lc app=leetcode.cn id=222 lang=typescript
*
* [222] 完全二叉树的节点个数
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function countNodes(root: TreeNode | null): number {
// 边界条件,如果root不存在,则返回0
if(!root) return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
};
// @lc code=end
二叉搜索树:右子树上所有的值都会大于根节点的值,左子树上的所有节点都会小于根节点的值。所以,二叉搜索树的中序遍历输出一定是一个有序数组。
由于这是一颗二叉搜索树,那么左子树的树肯定小于根节点,右子树肯定大于根节点
从这个特性我们就可以得出:
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
// 计算节点数量
function getCount(root: TreeNode): number {
if(!root) return 0;
return getCount(root.left) + getCount(root.right) + 1;
}
// 由于这是一颗二叉搜索树,那么左子树的树肯定小于根节点,右子树肯定大于根节点
// 从这个特性我们就可以得出:
// 如果要找第k大的树,并且k小于或者等于右子树节点数量的话,那我们可以先到右子树中去查找,
// 如果k等于右子树节点数量加1,那么说明我们要找的数其实就是根节点
// 如果不是上述两种情况,那么,我们就直接在左子树中查找,不过此时,因为我们已经排除了根节点和右子树了,就不再是找左子树第k大的节点了,我们应该减去根节点和右子树节点的数量
function kthLargest(root: TreeNode | null, k: number): number {
// 获取右子树节点数量
const count_r = getCount(root.right);
// 如果k小于等于右子树节点数量,则在右子树上查找第k大的树
if(k<=count_r) return kthLargest(root.right, k);
// 如果k等于右子树节点数量加1,说明要找的树就是根节点
if(k===count_r+1) return root.val;
// 否则去左子树查找第k-(count_r+1)=k- count_r - 1 大的数
return kthLargest(root.left, k - count_r - 1);
};
这道题我们其实就是在边界条件处理好之后,看一下 B 能否匹配一整颗 A 树,如果不能匹配,那么就看你一下 B 能不能匹配 A 的左子树或者其右子树,如果能够匹配上任意一个,就说明 B 是 A 的子结构。
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
// 递归匹配子树结构
function isMatch(a: TreeNode, b: TreeNode) {
// 如果b为空,肯定匹配,返回true
if(!b) return true;
// 如果a为空则不可能匹配,返回false
if(!a) return false;
// 如果a和b的值不一致,返回false
if(a.val !== b.val) return false;
// 分别递归匹配a、b的左子树和右子树
return isMatch(a.left, b.left) && isMatch(a.right, b.right);
}
function isSubStructure(A: TreeNode | null, B: TreeNode | null): boolean {
// 依据题意,如果B为空,则不是任何树的子结构,直接返回false
if(!B) return false;
// 若A为空,B不为空,则不可能匹配上,返回false
if(!A) return false;
// 递归判断A、B是否能够匹配,如果匹配上了,则返回true
if(isMatch(A, B)) return true;
// 上面的情况都不是,那么就分别去A的左子树和右子树看一下能否匹配,只要二者有一个匹配就算匹配成功
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
};
这道题在实现上确实是有一定的难度,我们可以借助上一篇文章中[《你真的了解二叉树吗(树形结构基础篇)》] 讲过的完全二叉树的编号方式以及一个队列来辅助我们完成这个题目。
在完全二叉树的编号中,假如当前节点的编号为 i,那么他的左子树的编号为 2*i,右子树的编号为 2*i+1,不过这题中,有可能会因为编号过大导致整形数据溢出的情况,所以我们需要特殊处理。具体的解题思路,代码注释写得很清楚,直接看代码吧。
/*
* @lc app=leetcode.cn id=662 lang=typescript
*
* [662] 二叉树最大宽度
*/
// @lc code=start
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
// 为了方便存储节点和它对应编号,定义一个包装类
class Wrapper {
constructor(public node: TreeNode, public idx: number){
}
}
function widthOfBinaryTree(root: TreeNode | null): number {
// 用来存储结果
let res = 0;
// 定义一个包装类的队列,这个包装类可以存储节点和他对应的编号
const queue: Wrapper[] = [];
// 先将根节点和他的编号0加入队列
queue.push(new Wrapper(root, 0));
// 用于存储每一层有多少个节点
let rowSize;
while(rowSize = queue.length) {
// 设置当前层最小编号和最大编号的初始值都为父节点的编号
let lIdx = queue[0].idx, rIdx = queue[0].idx;
// 遍历当前层的每一个元素
while(rowSize--) {
// 先将队首元素出队
const { node, idx } = queue.shift();
// 当前层最大编号为父节点的编号
rIdx = idx;
// 当存在左子树时,使用左子树根节点和其对应的编号创建包装类,并加入到队列
// 其实这里,最难的就是这个编号如何界定。
// 我们有学过完全二叉树的编号方式,可以参考这种编号方式,
// 第i个节点的做子树根节点的编号为2*i,右子树的编号为2*i+1
// 所以,到了这里,我们就直接使用idx * 2和idx * 2 + 1代表左子树和右子树编号
// 如果我们这么提交的话,会遇到一个问题,就是当我们的树的节点足够大时,这个编号会超出
// 我们整数的范围,那么,我们要怎样在不影响程序运行结果的前提下,缩减这个编号,避免整数溢出呢
// 我们来想想,对于一个编号来说,其实最小和最大编号分别为6和7,其实跟为0和1是没有本质上的区别的
// 因为我们拿这个编号并没有实质的作用,只是为了用这个编号计算差值,7-6的差值跟1-0的差值是一样的
// 所以,我们这边直接让当前的编号减去父节点的最小编号lIdx,以此来缩减编号大小
node.left && queue.push(new Wrapper(node.left, (idx - lIdx) * 2));
node.right && queue.push(new Wrapper(node.right, (idx - lIdx) * 2 + 1));
}
// 一层循环结束后,我们只要计算上一个宽度,与当前层最大编号减去最小编号加1,然后两者取最大值找到最大宽即可
// 例如:当前层最小和最大的编号是:0和8,那么这一层其实有8-0+1=9个节点空间,也就是宽度为9.
res = Math.max(res, rIdx - lIdx + 1);
}
return res;
};
// @lc code=end
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/rrOrY14eOYEl14jKD4yFbA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。