前端中的一些面试-算法与数据结构

时间复杂度

  • O(1) 一次就够(数量级)
    ex: 只走了一次处理逻辑

    const func = (a) => {
    	return a
    }
    
  • O(n) 和传输的数据量一样
    ex:时间复杂度和当前arr的长度一致

    const func = (arr) => {
    	for(let i = 0; i < arr.length; i++){
    		console.log(arr[i])
    	}
    }
    
  • O(n^2) 数据量的平方
    ex

    const func = (arr) => {
    	for(let i = 0; i < arr.length; i++){
    		for(let j = 0; j < arr.length; j++){
    			console.log(arr[j])
    		}
    	}
    }
    
  • O(logn) 数据量的对数(数量级) 100 => 10
    ex: 二分算法,每次把数据砍掉二分之一。
    [1,2,3,4,5,6,7,8,…] 找到6,可以通过砍掉中间的来比对

  • O(nlogn)

空间复杂度

  • O(1) 一次就够(数量级)
    ex: 像这样,不管数组的长度是多少,数组的内存大小是相对固定的
    const func = (arr: []) => {
    	arr[1] = 1
    	arr[2] = 2
    	arr[3] = 3
    	arr[4] = 4
    	return a
    }
    
  • O(n)
    ex:这里定义了一个新的数组,并且对相关的数组进行了赋值
    const func = (arr) => {
    	let arr2 = []
    	for(let i = 0; i < arr.length; i++){
    		arr2[i] = arr[i]
    	}
    }
    

单元测试

jest
判断数组是否相等 toEquel
判断bool是否相等 toBe

二分查找

/**
 * desc: 给一个有序的数组,[10, 20, 50, 70, 90] 查到50下标
 * 算法:二分法查找
 */

// 循环的方式
export const binarySearch1 = (arr: number[], target: number) => {
    let startIndex: number = 0
    let endIndex: number = arr.length - 1

    while (startIndex < endIndex) {
        let mdIndex = Math.floor((endIndex + startIndex) / 2)
        let value = arr[mdIndex]

        // 如果正好是中间的数字
        if (value === target) return mdIndex

        // 在左边
        if (target < value) {
            endIndex = mdIndex - 1
        }

        // 在右边
        if (target > value) {
            startIndex = mdIndex - 1
        }

    }

    return -1
}

// 递归的方式
const binarySearch2 = (arr: number[], target: number, startIndex?: number, endIndex?: number): number => {
    if(arr.length <= 0) return -1
    if(startIndex == null) startIndex = 0
    if(endIndex == null) endIndex = arr.length - 1
    
    if(startIndex > endIndex) return -1

    // 找到中间下标和value
    let midIndex = Math.floor((startIndex + endIndex) / 2)
    let midValue = arr[midIndex]

    if (midValue > target) {
        return binarySearch2(arr, target, startIndex, midIndex - 1)
    }else if(midValue < target){
        return binarySearch2(arr, target, midIndex + 1, endIndex)
    }else{
        return midIndex
    }
}

const arr = [10, 20, 40, 50, 70, 90, 120]
let res = binarySearch2(arr, 90)
console.log(res, 'res');

这里写了两种方式,递归和循环都可以完成问题,时间复杂度为O(logn),但是如果一定得使用最好的方式,循环更好,因为递归调用方法也会耗时。

将一个数组旋转k步

方案一 unshift pop
时间复杂度:O(n^2)
unshift 也是一个O(n) 的结构

方案二 contact
时间复杂度:O(1)

/**
 * descripbe: 
 * 1. 完成两个算法,k位数往数组的前面加
 * 2. 测试时间 log time endTime
 */


// 通过unshift的方法 [1,2,3,4,5,6,7] 3 => [5,6,7,1,2,3,4]
export const rodate1 = (arr: number[], len: number): number[] => {
    // 去k的绝对值
    let k = Math.abs(len % arr.length)
    for (let i = 0; i < k; i++) {
        let chu = arr.pop()
        if (chu) {
            arr.unshift(chu)
        }
    }

    return arr
}

// 通过contact的方法
export const rodate2 = (arr: number[], len: number): number[] => {
    let k = Math.abs(len % arr.length)
    len = k
    let l = arr.length

    let left = arr.slice(0, l - len)
    let right = arr.slice(l - len, l)

    return [...right, ...left]
}

let arrHuge = []
for (let i = 0; i < 100000; i++) {
    arrHuge.push(i)
}
let step = 8 * 10000

// 这个过程执行了 4000ms 也就是4s 时间复杂度 O(n^2)
console.time()
rodate1(arrHuge, step)
console.timeEnd()

// 这个过程仅仅2.7毫秒 时间复杂度 O(1)
console.time()
rodate2(arrHuge, step)
console.timeEnd()

数据结构

栈的应用

判断是否匹配 夸号类型

/**
 * descripbe: 算法 {a[b(c)d]e}
 * 判断是否匹配 夸号类型
 */
const isMatch = (top: string, s: string): boolean => {
    if (top === "{" && s === '}') return true
    if (top === "(" && s === ')') return true
    if (top === "[" && s === ']') return true
    return false
}

export const matchFunc = (str: string) => {
    let length = str.length
    if (length <= 0) return false
    let leftSyb = '{[('
    let rightSyb = '}])'
    let stack = []

    for (let i = 0; i < length; i++) {
        let s = str[i]
        if (leftSyb.includes(s)) {
        	// 碰到左边的字符串先入栈
            stack.push(s)
        } else if (rightSyb.includes(s)) {
        	// 碰到右边的字符串先判断是否和顶层的匹配
            let top = stack[stack.length - 1]

            if (isMatch(top, s)) {      
            	// 匹配的话,就删除顶层
                stack.pop()
            } else {
                return false
            }
        }
    }

    return stack.length === 0
}

let str1 = '{1[2(3)4]5}' // 匹配
let str2 = '{1[2(34]5}19)' // 顺序不一样
console.log(matchFunc(str1),matchFunc(str2), // true, false
找数组中两个字符串相加为10的值

O(n^2)的方式两个循环如下:

/**
 * desc: 找数组中两个字符串相加为10的一个值
 */
export const twoNumberAdd = (arr: number[], total: number): number[] => {
    if(arr.length === 0) return []
    if(total === 0) return []
    let length = arr.length
    const res: number[] = []
    let flag = false

    for (let i = 0; i < length - 1; i++) {
        for (let j = i; j < length - 1; j++) {
            if (arr[i] + arr[j] == total) {
                res.push(arr[i])
                res.push(arr[j])
                flag = true
                break;
            }
        }
        if (flag) break // for循环中,break可以停止循环。
    }
    return res
}

const arrlog = [1,2,3,4,5,6,8]
const res = twoNumberAdd(arrlog, 10)
console.log(res, 'res');

二分理念去查,时间复杂度 O(N)

export const twoNumberAdd2 = (arr: number[], total: number): number[] => {
    if(arr.length === 0) return []
    if(total === 0) return []
    const res: number[] = []
    let i = 0
    let j = arr.length - 1

    while (i < j) {
        let a = arr[i]
        let b = arr[j]
        let t = a + b

        if (t > total) {
            j--
        } else if (t < total) {
            i++
        } else {
            res.push(a)
            res.push(b)
            break
        }
    }

    return res
}


const arrlog = [1, 2, 3, 4, 5, 6, 8]
const res = twoNumberAdd2(arrlog, 10)
console.log(res, 'res');

时间复杂度对比,可以看出,二分法的时间复杂度更低。选第二个方法。

const arrlog = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 4, 5, 6, 8]
console.time()
for (let i = 0; i < 100 * 10000; i++) {
    twoNumberAdd(arrlog, 10)
}
console.timeEnd()


console.time()
for (let i = 0; i < 100 * 10000; i++) {
    twoNumberAdd2(arrlog, 10)
}
console.timeEnd()

// default: 288.071044921875 ms
// default: 14.84619140625 ms
链表

js实现单向链表结构


// 实现一个单项链表功能
// 变成 {value: '100', next: { value: "200", next: { value: '300', next: { value: '400' } } }}
const arrInit = [100, 200, 300, 400, 500]
const func = (arr) => {
  let length = arr.length
  if (length <= 0) return [] 
  
  let curNode = {
    value: arr[length-1],
  }
  for (let i = length-2; i >= 0; i--) {
    console.log(i, 'i');
    curNode = {
      value: arr[i],
      next: curNode
    }
  }
  return curNode
}

console.log(func(arrInit), 'arr')

实现反转单向链表算法

/**
 * {value: 100, next: { value: 200, next: { value: 300, next: { value: 400, next: { value: 500 } } } }}
 * 处理成为下面结构
 * {value: 500, next: { value: 400, next: { value: 300, next: { value: 200, next: { value: 100 } } } }}
 * @param arr 反转单向链表结构结构
 */
interface INode {
    value: number,
    next?: INode
}
export const reverseLinkNode = (linkNode: INode) => {
    // 定义三个指针用于接收
    let prevNode: INode | undefined = undefined
    let curNode: INode | undefined = undefined
    let nextNode: INode | undefined = linkNode
    
    // 循环赋值引用
    while(nextNode){
        console.log('1');
        
        // 第一种情况
        if (curNode && !prevNode) {
            delete curNode.next
        }

        // 第二种和倒数第二种情况
        if (curNode && prevNode) {
            curNode.next = prevNode
        }

        prevNode = curNode
        curNode = nextNode
        // @ts-ignore
        nextNode = nextNode.next
    }

    
    curNode!.next = prevNode

    
    return curNode
}

const list = {value: 100, next: { value: 200, next: { value: 300, next: { value: 400, next: { value: 500 } } } }}
let list2 = reverseLinkNode(list)
console.log(list2, 'list2');

链表中,查询慢删除快
数组中,查询快删除慢。

队列

用两个栈实现队列(数组方式)

/**
 * 两个栈实现队列
 */
export class MyQuene {
    constructor(stack1: number[]){
        this.stack1 = stack1
    }
    stack1: number[] = []
    stack2: number[] = []

    add(n: number) {
        this.stack1.push(n)
    }
    delete(): number | null {
        let stack1 = this.stack1
        let stack2 = this.stack2

        // 1. 压栈处理成stack2
        while(stack1.length){
            let n = stack1.pop()
            if (n) stack2.push(n)
        }
        
        // 2. stack2.pop
        let deleteItem = stack2.pop()
        // 3. stack2 压栈成stack1
        while(stack2.length){
            let n = stack2.pop()
            if(n) stack1.push(n)
        }
        
        return deleteItem as number
    }
    get length() {
        return this.stack1.length
    }
}

const q = new MyQuene([1,2,3,4,5])
let dItem = q.delete()
console.log(dItem, q.stack1); // 1, [2,3,4,5]
q.add(6)						
console.log('add', q.stack1); // add, [2,3,4,5,6]
console.log(q.length);        // 5

大致思路如下:
首先有两个栈,一个栈用于存初始值。一个用于转换的时候用,stack1压栈成stack2,顺序改变,A在首部变成尾部,直接pop删除最后一个元素,再压栈成stack1 得到的就是 删掉了A的 BCDE。

一些数据量庞大的场景中,由于数组unshiftshift消耗的性能比较大时间复杂度(O(n^2)),使用栈的概念,可以通过 两个栈+队列 的方式完成数组的unshift操作。

用链表实现队列

/**
 * 链表实现队列
 */
interface INode {
    value: number,
    next: INode | null
}
export class MyQuene {
    head: INode | null = null
    tail: INode | null = null
    len: number = 0

    add(n: number) {
        let newNode: INode = {
            value: n,
            next: null
        }

        // 处理head
        if (this.head == null) {
            this.head = newNode
        }

        // // 处理tail
        let tailNode = this.tail
        if (tailNode) {
            // 第二次进入已经新add了一个newNode,可以指定next为新节点
            tailNode.next = newNode
        }
        this.tail = newNode

        this.len++
    }

    delete(): number | null {
        const headNode = this.head
        if (this.len == 0) return null
        if (this.head == null) return null

        let value = headNode!.value
        
        // 处理head
        this.head = headNode!.next

        this.len--
        
        return value

    }
    get length(): number {
        return this.len
    }
}
数组和链表哪个更快?
  • 数组是连续存储,push很快,shift很慢
  • 链表是非连续存储,add和delete都很快(查找很慢)
  • 所以链表实现队列更快
二叉树结构
题目:给一个有序的二叉树结构,找到第k位的树
/**
 * 二叉搜索树中的第k位
 */
interface INode {
    value: number,
    left: INode | null,
    right: INode | null
}
const bst: INode = {
    value: 5,
    left: {
        left: {
            value: 2,
            left: null,
            right: null
        },
        right: {
            value: 4,
            left: null,
            right: null
        },
        value: 3
    },
    right: {
        left: {
            value: 6,
            left: null,
            right: null
        },
        right: {
            value: 8,
            left: null,
            right: null
        },
        value: 7
    },
}


let arr: number[] = []

/**
 * 前序遍历
 * Preorder traversal
 */
const preorderTraversal = (node: INode | null) => {
    if (!node) return
    if (!node) return
    arr.push(node.value)
    preorderTraversal(node.left)
    preorderTraversal(node.right)
}

/**
 * 中序遍历
 * Preorder traversal
 */
const inorderTraversal = (node: INode | null) => {
    if (node === null) return
    inorderTraversal(node.left)
    arr.push(node.value)
    inorderTraversal(node.right)
}

/**
 * 后序遍历
 * Preorder traversal
 */
const postorderTraversal = (node: INode | null) => {
    if (!node) return
    if (!node) return
    postorderTraversal(node.left)
    postorderTraversal(node.right)
    arr.push(node.value)
}


export const biranyTreeSearch1 = (node: INode, k: number): number | null=> {
    inorderTraversal(node)

    return arr[k - 1] || null
}

console.log(biranyTreeSearch1(bst, 3));

单元测试
一般react-create-app有集成jest包,可以直接通过 yarn test 运行
describe:描述内容
it: 提示内容
expect: 期望的方法
toBe: 期望得到某个数字
toBeNull: 期望得到null

import { INode, biranyTreeSearch1, bst } from './index'

describe('二叉搜索树求key位的值', () => {
    it('普通情况', () => {
        expect(biranyTreeSearch1(bst, 3)).toBe(4)
    });
    it('为0的情况', () => {
        expect(biranyTreeSearch1(bst, 0)).toBeNull()
    });
})

// Test Suites: 1 passed, 1 total
// Tests:       2 passed, 2 total

三种遍历

  • 前序遍历
  • 中序遍历
  • 后序遍历
重点:
  • 二叉树和三种遍历
  • 二叉搜索树的特点:left <= root;right >= root
  • 二分搜索树的价值:可使用二分法进行快速查找

算法三大规则

  • 贪心
  • 二分
  • 动态规划
斐波拉切数列 找第 N 位

递归方式
这种方式 时间复杂度(O(n^2))

export function fiboracheSequence1(n: number): number {
    if (n <= 0) return 0
    if (n === 1) return 1

    return fiboracheSequence1(n - 1) + fiboracheSequence1(n - 2)
}

// 测试
console.log(fiboracheSequence1(8)) // 21

循环的方式

export function fiboracheSequence2(n: number): number {
    if (n <= 0) return 0
    if (n === 1) return 1

    let n1 = 1
    let n2 = 0
    let res = 0

    for (let i = 2; i < n; i++) {
        res = n1 + n2

        n2 = n1
        n1 = res
    }

    return res 
}
移动数组中的0到末尾

O(n^2) 不好用

export const moveNumber1 = (arr: number[]) => {
    if (arr.length === 0) return []

    let length = arr.length
    let blLen = 0
    for (let i = 0; i < length - blLen; i++) {
        if (arr[i] === 0) {
            
            arr.push(0)
            arr.splice(i, 1)
            i--
            blLen ++
        }
    }

    return arr
}

let initArr = [1, 0, 1, 0, 1, 0, 234, 1, 34521, 0, 1, 90, 0]
moveNumber1(initArr)
console.log(initArr, 'arr')

O(n)时间复杂度

// O(n)的时间复杂度
export const moveNumber2 = (arr: number[]) => {
    let i, j = -1, length = arr.length

    for (i = 0; i < length; i++){
        if (arr[i] === 0) {
            if (j < 0) {
                j = i
            }
        }
        if(arr[i] !== 0 && arr[j] >= 0){
            // 交换
            const n = arr[i]
            arr[i] = arr[j]
            arr[j] = n
            
            j++
        }
    }
}

let initArr = [1, 0, 1, 0, 1, 0, 234, 1, 34521, 0, 1, 90, 0]
moveNumber2(initArr)
console.log(initArr, 'arr')
字符串中连续最多的字符,以及次数

O(n)

// abcdddddfffg
interface IRes {
    char: string,
    length: number
}
export const findStr = (str: string) => {
    let length = str.length
    let res: IRes = {
        char: '',
        length: 0
    }
    let temLength

    for (let i = 0; i < length; i++) { 
        console.log('---------------');
        
        temLength = 0
        for (let j = i; j < length; j++) {
            console.log(`i:${i}\nj:${j}\ntemp:${temLength}\nres.length:${res.length}\nres.char:${res.char}`);
            if (str[i] === str[j]) {
                temLength++
            }
            if (str[i] !== str[j] || j === length - 1) {
                if (temLength > res.length) {
                    res.length = temLength
                    res.char = str[i]
                }
                if (i < length - 1) {
                    i = j - 1
                }

                break;
            }
        }
    }

    return res

}

let str = 'abcdddefg'
console.log(findStr(str));

O(n)的时间复杂度

思路: 通过判断当前元素和上一个元素是否相等,来决定要不要累加。


export const findStr2 = (str: string): IRes => {
    let length = str.length
    let res: IRes = {
        char: '',
        length: 0
    }
    if (length === 0) return res

    let tempLength = 1
    for (let i = 0; i < length - 1; i++){
        if (str[i] === str[i+1]) {
            tempLength++
        }else if(str[i] !== str[i+1]){
            if (tempLength > res.length) {
                res = {
                    char: str[i],
                    length: tempLength
                }
                tempLength = 1
            }
        }
    }

    return res
}

let str = 'abcdddeeeeeeefffg'
console.log(findStr2(str)); // char: e, length: 7

双指针


export const findStr3 = (str: string): IRes => {
    let length = str.length
    let res: IRes = {
        char: '',
        length: 0
    }
    if (length === 0) return res

    let tempLength = 0
    let i = 0,j = 0
    for (; i < length; i++){
        if (str[i] === str[j]) {
            tempLength++
        }else if(str[i] !== str[j] || i === length - 1){
            if (tempLength > res.length) {
                res = {
                    char: str[j],
                    length: tempLength
                }
            }
            tempLength = 0
            if (i < length - 1){
                j = i
                i --
            }
        }
    }
    

    return res
}

单元测试

import { findStr1, findStr2 } from './index'

describe('寻找重复的字串第一种方法:跳步', () => {
    it('普通情况', () => {
        expect(findStr1('abcdddedff')).toEqual({char: 'd', length: 3})
    })
    it('都是连续字符', () => {
        expect(findStr1('dddeeeeddd')).toEqual({char: 'e', length: 4})
    })
    it('字符串为空', () => {
        expect(findStr1('')).toEqual({char: '', length: 0})
    })
    it('无连续字符', () => {
        expect(findStr1('abcdefghijk')).toEqual({char: 'a', length: 1})
    })
})


describe('寻找重复的字串第二种方法:一次循环判断是否为相同字符', () => {
    it('普通情况', () => {
        expect(findStr2('abcdddedff')).toEqual({char: 'd', length: 3})
    })
    it('都是连续字符', () => {
        expect(findStr2('dddeeeeddd')).toEqual({char: 'e', length: 4})
    })
    it('字符串为空', () => {
        expect(findStr2('')).toEqual({char: '', length: 0})
    })
    it('无连续字符', () => {
        expect(findStr2('abcdefghijk')).toEqual({char: 'a', length: 1})
    })
})


快速排序
// 快速排序 slice
export const quickSort1 = (arr: number[]): number[] => {
    let length = arr.length

    if (length === 0) return []

    let left = [], right = []
    let midIndex = Math.floor(arr.length / 2)
    let midValue = arr.slice(midIndex, midIndex + 1)[0]
    
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < midValue) {
            left.push(arr[i])
        } else if (arr[i] > midValue) {
            right.push(arr[i])
        }
    }
    
    return quickSort1(left).concat([midValue], quickSort1(right))
}

// 快速排序 splice
export const quickSort2 = (arr: number[]): number[] => {
    let length = arr.length

    if (length === 0) return []

    let left = [], right = []
    let midIndex = Math.floor(arr.length / 2)
    let midValue = arr.splice(midIndex, 1)[0]
    
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < midValue) {
            left.push(arr[i])
        }
        if (arr[i] > midValue) {
            right.push(arr[i])
        }
    }

    return quickSort2(left).concat([midValue], quickSort2(right))
}

const arr = [4,5,7,1,2,3,6,3,15,5,123,8,9,1,9,8]
console.log(quickSort2(arr), 'arr')
求回文数(aba 121 454)
// 求一个范围内的回文数 转字符串 转数字判断是否相等
export const getPalindromeNumberFunc1 = (max: number): number[] => {
    let res: number[] = []
    if (max <= 0) return []

    for (let i = 1; i <= max; i++) {
        let n = i.toString()
        if (n === n.split('').reverse().join('')) {
            res.push(i)
        }
    }

    return res
}

// 求一个范围内的回文数 依次判断首位和末尾是否一直相等
export const getPalindromeNumberFunc2 = (max: number): number[] => {
    let res: number[] = []
    if (max <= 0) return []

    for (let i = 1; i <= max; i++) {
        let n = i.toString()
        let startIndex = 0
        let endIndex = n.length - 1
        let flag = true

        while (startIndex < endIndex) {
            if (n[startIndex] === n[endIndex]) {
                startIndex++
                endIndex--
            } else {
                flag = false
                break
            }
        }

        if (flag) {
            res.push(i)
        }
    }

    return res
}

// 求一个范围内的回文数 反转数字
export const getPalindromeNumberFunc3 = (max: number): number[] => {
    let res: number[] = []
    if (max <= 0) return []

    for (let i = 1; i <= max; i++) {
        let n = i
        let rev = 0

        while (n > 0) {
            rev = rev * 10 + n % 10
            n = Math.floor(n / 10)
        }

        if (rev === i) res.push(i)
    }

    return res
}

console.log(getPalindromeNumberFunc2(500));

测试效果:方法1:400ms;方法二:50ms;方法三:42ms
时间复杂度分析:方法一最慢,因为数组转换需要时间

数字转成千分位的字符串

export const numberToStr = (num: number): string => {
    
    let res = ''
    let str = num.toString()
    let length = str.length
    let times = 0

    for (let i = length - 1; i >= 0; i--) {
        times ++
        
        if (times == 3 && i !== 0) {
            res = ',' + str[i] + res
            times = 0
        }else{
            res = str[i] + res
        }
    }
    return res
}

console.log(numberToStr(13880000000));

大小写转换
// 正则表达式大小写转换
export const toggleCase1 = (str: string): string => {
    let res = ''
    let length = str.length

    for (let i = 0;i < length; i++){
        
        let s = str[i]
        let reg1 = /[a-z]/
        let reg2 = /[A-Z]/
        
        if (reg1.test(s)) {
            res = res + s.toUpperCase()
        }else if(reg2.test(s)){
            res = res + s.toLowerCase()
        }else {
            res = res + s
        }
    }
    return res
}

// 通过ASCII编码
export const toggleCase2 = (str: string): string => {
    let res = ''
    let length = str.length

    for (let i = 0;i < length; i++){
        let s = str[i]
        let code = s.charCodeAt(0)

        if (code >= 65 && code <= 90) {
            res = res + s.toLowerCase()
        }else if(code >= 97 && code <= 122){
            res = res + s.toUpperCase()
        }else {
            res = res + s
        }
    }
    
    return res
}

console.log(toggleCase2('avheDF!DsadSFEWF'));

0.1 + 0.2 !== 0.3
整数转换二进制没有误差
有些小数可能是无法用二进制精准转化
各个计算机语言的通病
通过math.js 进行准确的计算

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值