必知必会:传统算法【Java实现】

目录

一、排序

1. 冒泡排序

2. 插入排序

3. 希尔排序

4. 选择排序

5. 快速排序

6. 堆排序

7. 归并排序

8. 桶排序、基数排序、计数排序(从略)

二、 递归/搜索

1. 栈的逆序,递归写法

2. 实现汉诺塔 [ LintCode 169 ]

3. 数组的全排列 [ LeetCode 46/47 ]

三、 链表题

单链表翻转

四、 二叉树

1. 前序遍历非递归实现 [ LeetCode 144 ]

2. 中序遍历非递归实现 [ LeetCode 094 ]

3. 后序遍历非递归实现 [ LeetCode 145 ]

4. 给前序和中序,求二叉树

五、 二分法


本文主要使用Java语言来实现编程中的一些必知必会传统算法。

一、排序

普通排序类算法,可以去[ LintCode 463/464 ]处练习,校对。

1. 冒泡排序

带有flag标志的冒泡排序算法如下:
public class Solution {
    /**
     * @param A: an integer array
     * @return: nothing
     */
    public void bubbleSort(int[] A) {
        int n = A.length;
        for(int i = n-1; i > 0; i--){
            boolean f = true;
            for(int j = 0; j < i; j++){
                if(A[j] > A[j+1]){
                    int t = A[j];
                    A[j] = A[j+1];
                    A[j+1] = t;
                    f = false;
                }
            }
            if(f) break;
        }
    }
}
双端冒泡排序(鸡尾酒排序)实现如下。
public class Solution {
    public void biBubbleSort(int[] A) {
        int n = A.length;
        int low = 0, idx = 0;
        int high = n - 1;
        while(low < high){
            for(int i = low; i < high; i++){
                if(A[i] > A[i+1]){
                    int t = A[i];
                    A[i] = A[i+1];
                    A[i+1] = t;
                    idx = i;
                } 
            }
            high = idx;
            for(int i = high; i > low; i--){
                if(A[i] < A[i-1]){
                    int t = A[i];
                    A[i] = A[i-1];
                    A[i-1] = t;
                    idx = i;
                }
            }
            low = idx;
        }
    }
}

二者的时间复杂度均为O(N2)。

双端冒泡排序参考自:https://blog.csdn.net/qq_35433716/article/details/83957847

2. 插入排序

以Java为例,代码如下;注意Java中的数组下标从0开始编号。
public class Solution {
    public void insertSort(int[] A) {
        int n = A.length;
        for(int i = 1; i < n; i++){
            int t = A[i];
            int j = i - 1;
            for(; j >= 0 && A[j] > t; j--) A[j+1] = A[j];
            A[j+1] = t;
        }
    }
}

3. 希尔排序

void shell_sort(int arr[], int len) {
	int gap, i, j;
	int temp;
	for (gap = len >> 1; gap > 0; gap >>= 1)
		for (i = gap; i < len; i++) {
			temp = arr[i];
			for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
				arr[j + gap] = arr[j];
			arr[j + gap] = temp;
		}
}
使用Java的实现代码如下:
public class Solution {
    public void shellSort(int[] A) {
        int n = A.length;
        int gap, i, j;
        int temp;
        for(gap = n >> 1; gap > 0; gap >>= 1){
            for(i = gap; i < n; i++){
                temp = A[i];
                for(j = i - gap; j >= 0 && A[j] > temp; j -= gap)
                    A[j + gap] = A[j];
                A[j + gap] = temp;
            }
        }
    }
}

4. 选择排序

选择排序的通用算法框架(C/C++描述)如下:
void selectSort(int arr[])
{
    for(int i = 0; i < arr.length; ++i)   // 选择第i小的记录,并交换到位
    {
        int j = selectMin(arr, i);  // 在arr[i.. arr.length]中选择key最小的记录
        if(i != j) swap(arr[i], arr[j]);  // 与第i个记录交换
    }
}
使用C/C++更详细的实现如下:
void selectSort(int *arr)
{
	for(int i = 0; i < arr.length; ++i){
		int minIdx = i;
		for(int j = i + 1; j < arr.length; j++) {
            if(arr[minIdx] > arr[j]) {
                minIdx = j;
            }
        }
	}
	if(i != minIdx) swap(arr[i], arr[minIdx]);
}
使用Java的实现代码如下:
public class Solution {
    public void selectSort(int[] A) {
        int n = A.length;
        for(int i = 0; i < n; i++){
            int minIdx = i;
            for(int j = i + 1; j < n; j++){
                if(A[j] < A[minIdx]){
                    minIdx = j;
                }
            }
            if(i != minIdx){
                int t = A[i];
                A[i] = A[minIdx];
                A[minIdx] = t;
            }
        }
    }
}

5. 快速排序

快排递归版Java实现代码如下:
public class Solution {
    public void sortIntegers2(int[] A) {
        quickSort(A, 0, A.length-1);
    }
    private void quickSort(int [] A, int left, int right){
        if(left > right) return;
        int low = left, high = right;
        int key = A[low];
        while(left < right){
            while(left < right && A[right] >= key) --right;
            A[left] = A[right];
            while(left < right && A[left] <= key) ++left;
            A[right] = A[left];
        }
        A[right] = key;
        quickSort(A, low, left - 1);
        quickSort(A, left + 1, high);
    }
}

快排非递归版C/C++实现如下:
int partition(int *a, int low, int high){
	int p = a[low];
	while(low < high){
		while(low < high && a[high] >= p) --high;
		a[low] = a[high];
		while(low < high && a[low] <= p) ++low;
		a[high] = a[low];
	}
	a[low] = p;
	return low;
}
void quicksort(int *a, int left, int right){
	stack<int> temp;
	int i, j;
	temp.push(right);
	temp.push(left);
	while(!temp.empty()){
		i = temp.top();
		temp.pop();
		j = temp.top();
		temp.pop();
		if(i >= j) continue;
		int k = partition(a, i, j);
		if(k > i){
			temp.push(k-1);
			temp.push(i);
		}
		if(k < j){
			temp.push(j);
			temp.push(k+1);
		}
	}
}

快排非递归版Java实现如下:
import java.util.Stack;
public class Solution {
    public void sortIntegers2(int[] A) {
        quickSort(A, 0, A.length-1);
    }
    private int partition(int [] A, int low, int high){
        int p = A[low];
        while(low < high){
            while(low < high && A[high] >= p) -- high;
            A[low] = A[high];
            while(low < high && A[low] <= p) ++ low;
            A[high] = A[low];
        }
        A[low] = p;
        return low;
    }
    private void quickSort(int [] A, int left, int right){
        Stack<Integer> temp = new Stack<Integer>();
        int i, j;
        temp.push(right);
        temp.push(left);
        while(!temp.empty()){
            i = temp.pop();
            j = temp.pop();
            if(i >= j) continue;
            int k = partition(A, i, j);
            if(k > i){
                temp.push(k-1);
                temp.push(i);
            }
            if(k < j){
                temp.push(j);
                temp.push(k+1);
            }
        }
    }
}

 

6. 堆排序

完美二叉树(即:满二叉树),定义从略。完全二叉树:除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐。完满二叉树(Full Binary Tree/Strictly Binary Tree):所有非叶结点的度都是2。

堆排序算法借用了完全二叉树结点编号的一个特征,假设根节点编号为0,这也契合数组的第一个元素下标为0,则左子树编号为2*0+1,右子树编号为2*0+2,一般地,设父结点编号为A,则左子树根节点编号为2*A+1,右子树根结点编号为:2*A+2。

下述代码中,a为待排序数组,在heapAdjust()函数中,数组a[s..m]中除s结点之外均满足堆的定义。

public class Solution {
    public void heapsort(int[] A) {
        int n = A.length;
        if(n == 0) return;
        for(int i = n / 2; i >= 0; i--){
            heapAdjust(A, i, n-1);   // 初始建堆,从最后一个非叶节点开始
        }
        for(int i = n-1; i > 0; i--){
            int t = A[0];
            A[0] = A[i];
            A[i] = t;               // 将堆顶值(数组首元素)交换至数组尾部
            heapAdjust(A, 0, i - 1);
        }
    }
    private void heapAdjust(int [] a, int s, int m){
        int t = a[s];
        int i = 2 * s + 1;
        while(i <= m){
            if(i < m && a[i] < a[i+1]) ++i; // 找到左右子树根节点的最大值
            if(t >= a[i]) break;     // 已满足堆定义,无需再向下筛
            a[s] = a[i];              // 使s结点满足堆定义
            s = i;                    // 使s结点满足堆定义
            i = i * 2 + 1;
        }
        a[s] = t;
}
// 大顶/根堆调整算法递归版
    private void heapAdjust2(int [] a, int s, int m){
        int t = a[s];
		int i = 2*s + 1;
		if(i > m) return;
		if(i < m && a[i] < a[i+1]) ++i;
		if(t >= a[i]) return;
		else{
		    a[s] = a[i];
		    a[i] = t;
		}
		heapAdjust2(a, i, m);
    }
}

7. 归并排序

本实现给出递归版与非递归版两种,非递归版归并排序仅仅是mergeSort函数的不同,以C/C++语言为例。

void merge(int *a, int low, int mid, int high){
	int i, j, k;
	int b[high - low + 1]; // 中间过程数组
	i = low; j = mid + 1;
	for(k=0; i <= mid && j <= high; k++){
		if(a[i]<a[j]) b[k]=a[i++];
		else b[k] = a[j++];
	}
	while(i<=mid) b[k++] = a[i++];
	while(j<=high) b[k++] = a[j++];
	for(i=low, j=0; i <= high; i++, j++)
		a[i] = b[j];
}

void mergeSort(int a[], int low, int high){
	if(low < high){
		int mid = (low + high) / 2;
		mergeSort(a, low, mid);
		mergeSort(a, mid + 1, high);
		merge(a, low, mid, high);
	}
}
// 非递归版的mergeSort2()函数如下
void mergeSort2(int a[], int n){
	int step = 1; // 步长分别为1,2,4,8…
	while(step <= n){
		int low = 0;
		while(low + step <= n){
			int mid = low + step - 1;
			int high = mid + step;
			if(high > n) high = n;
			merge(a, low, mid, high);
			low = high + 1;
		}
		step *= 2
	}
}
当 int a[8] = {1, 6, 3, 2, 9, 8, 7, 5}; 此时,int n = 8;
递归版调用方式为:mergeSort(a, 0, n-1);
非递归调用方式为:mergeSort2(a, n-1);
使用Java实现的归并算法代码如下:
public class Solution {
    public void sortIntegers2(int[] A) {
        // mergeSort(A, 0, A.length - 1);
        mergeSort2(A, A.length - 1);
    }
    private void mergeSort(int[] a, int low, int high){
        if(low < high){
            int mid = (low + high) / 2;
            mergeSort(a, low, mid);
            mergeSort(a, mid + 1, high);
            merge(a, low, mid, high);
        }
    }
    private void merge(int[] a, int low, int mid, int high){
        int i, j, k;
        int[] b = new int[high - low + 1];
        i = low;
        j = mid + 1;
        for(k = 0; i <= mid && j <= high; k++){
            if(a[i] < a[j]) b[k] = a[i++];
            else b[k] = a[j++];
        }
        while(i <= mid) b[k++] = a[i++];
        while(j <= high) b[k++] = a[j++];
        for(i = low, j = 0; i <= high; i++, j++){
            a[i] = b[j];
        }
    }
    
    private void mergeSort2(int[] a, int n){
        int step = 1;
        while(step <= n){
            int low = 0;
            while(low + step <= n){
                int mid = low + step - 1;
                int high = mid + step;
                if(high > n) high = n;
                merge(a, low, mid, high);
                low = high + 1;
            }
            step *= 2;
        }
    }
}

8. 桶排序、基数排序、计数排序(从略)

 

二、 递归/搜索

1. 栈的逆序,递归写法

在考虑递归解法的时候,需要铭记的一点是:发现原问题中的子问题。就本问题而言,子问题就是可以考虑成以下两种形式:

(1) 取出栈顶元素后,将栈进行逆序,最后将取出的栈顶元素插入到栈底;

(2) 取出栈底元素后,将栈进行逆序,最后将取出的栈底元素压入到栈顶。

public class Solution{
    public void reverseStack(Stack stack){
        if(stack.empty()) return;
        int top = stack.pop();
        reverseStack(stack);
        insert2StackBottom(stack, top);
    }
    public void insert2StackBottom(Stack stack, int bottom){
        if(stack.empty()) stack.push(bottom);
        int top = stack.pop();
        insert2StackBottom(stack, bottom)
        stack.push(top);
    }
}

方法insert2StackBottom的子问题为:将栈顶元素取出,将待插入元素插入到栈的栈底,将取出的栈顶元素压入。

本题参考自:https://blog.csdn.net/dm_vincent/article/details/8008238

另有相类似的递归练习题:字符串逆序,逆序打印单链表 等等。

2. 实现汉诺塔 [ LintCode 169 ]

非常经典的递归应用练习题,应深刻理解、掌握、应用。
public class Solution {
    /**
     * @param n: the number of disks
     * @return: the order of moves
     */
    private List ans = new ArrayList();
    private void hanoi(int n, char x, char y, char z){
        if(n == 1){
            String t = "from " + x + " to " + z;
            ans.add(t);
        }
        else{
            hanoi(n-1, x, z, y);
            String t = "from " + x + " to " + z;
            ans.add(t);
            hanoi(n-1, y, x, z);
        }
    }
    public List<String> towerOfHanoi(int n) {
        hanoi(n, 'A', 'B', 'C');
        return ans;
    }
}

3. 数组的全排列 [ LeetCode 46/47 ]

求数组全排列有递归迭代两种解法,数组中含与不含重复数字时解法有所不同,这里当前仅辑录递归解法,迭代解法待加。

数组中不含重复数字的解法如下:
class Solution {
    private List<List<Integer>> ans = new ArrayList<List<Integer>>();
    private void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
    private void getPermutation(int[] arr, int n, int idx){
        if(idx == n){
            List<Integer> tmp = new ArrayList<Integer>();
            for(int e: arr)
                tmp.add(e);
            ans.add(tmp);
        }
        else{
            for(int i = idx; i < n; i++){
                swap(arr, i, idx);
                getPermutation(arr, n, idx + 1);
                swap(arr, i, idx);
            }
        }
    }
    public List<List<Integer>> permute(int[] nums) {
        getPermutation(nums, nums.length, 0);
        return ans;
    }
}
数字中含重复数字的解法如下:
class Solution {
    private List<List<Integer>> ans = new ArrayList<List<Integer>>();
    private void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
    private void getPermutation(int[] arr, int n, int idx){
        if(idx == n){
            List<Integer> tmp = new ArrayList<Integer>();
            for(int e: arr)
                tmp.add(e);
            ans.add(tmp);
        }
        else{
            for(int i = idx; i < n; i++){
                boolean f = false;
                for(int j = i+1; j < n; j++){
                    if(arr[i] == arr[j]){
                        f = true;
                        break;
                    }
                }
                if(f) continue;
                swap(arr, i, idx);
                getPermutation(arr, n, idx + 1);
                swap(arr, i, idx);
            }
        }
    }
    public List<List<Integer>> permuteUnique(int[] nums) {
        getPermutation(nums, nums.length, 0);
        return ans;
    }
}

三、 链表题

单链表翻转

本题有多种解法,至少有四种解法,修改指针法,栈储存节点法,递归法、修改链表节点值法。这里仅给出递归法。[ LeetCode 206/LintCode 35 ]

优雅版递归解法如下:
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null)
            return head;
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

四、 二叉树

1. 前序遍历非递归实现 [ LeetCode 144 ]

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root == null) return new ArrayList();
        List<Integer> ans = new ArrayList<Integer>();
        Stack<TreeNode> st = new Stack<TreeNode>();
        TreeNode cur = root;
        while(cur != null || !st.empty()){
            if(cur != null){
                st.push(cur);
                ans.add(cur.val);
                cur = cur.left;
            }
            else{
                TreeNode t = st.pop();
                cur = t.right;
            }
        }
        return ans;
    }
}

 

2. 中序遍历非递归实现 [ LeetCode 094 ]

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root == null) return new ArrayList();
        List<Integer> ans = new ArrayList<Integer>();
        Stack<TreeNode> st = new Stack<TreeNode>();
        TreeNode cur = root;
        while(cur != null || !st.empty()){
            if(cur != null){
                st.push(cur);
                cur = cur.left;
            }
            else{
                TreeNode t = st.pop();
                ans.add(t.val);
                cur = t.right;
            }
        }
        return ans;
    }
}

3. 后序遍历非递归实现 [ LeetCode 145 ]

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        if(root == null) return new ArrayList();
        List<Integer> ans = new ArrayList<Integer>();
        Stack<TreeNode> st = new Stack<TreeNode>();
        TreeNode cur = root;
        TreeNode lastVisited = null;
        while(cur != null || !st.empty()){
            if(cur != null){
                st.push(cur);
                cur = cur.left;
            }
            else{
                TreeNode t = st.peek();
                if(t.right != null && t.right != lastVisited)
                    cur = t.right;
                else{
                    ans.add(t.val);
                    st.pop();
                    lastVisited = t;
                }
            }
        }
        return ans;
    }
}

4. 给前序和中序,求二叉树

已知:前序 + 中序 => 后序 [LeetCode 105/LintCode 73];

后序 + 中序 => 前序 [LeetCode 106/LintCode 72]。

务必要注意数组切片时其中的下标关系,不能出现丁点错误,否则各种Bug。下面Java代码中的做法不很高效,有更高效的解法,可做尝试。

给前序与中序,求二叉树,代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Arrays;
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || preorder.length == 0)
            return null;
        if(inorder == null || inorder.length == 0)
            return null;
        int tmp = preorder[0];
        TreeNode root = new TreeNode(tmp);
        int mid = 0;
        for(int i = 0; i < inorder.length; i++){
            if(tmp == inorder[i]){
                mid = i;
                break;
            }
        }
        int[] l1 = Arrays.copyOfRange(preorder, 1, mid+1);
        int[] r1 = Arrays.copyOfRange(inorder, 0, mid);
        root.left = buildTree(l1, r1);
        int[] l2 = Arrays.copyOfRange(preorder, mid+1, preorder.length);
        int[] r2 = Arrays.copyOfRange(inorder, mid+1, inorder.length);
        root.right = buildTree(l2, r2);
        return root;
    }
}

给中序和后序,求二叉树,代码如下:

import java.util.Arrays;
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder == null || inorder.length == 0)
            return null;
        if(postorder == null || postorder.length == 0)
            return null;
        int tmp = postorder[postorder.length-1];
        TreeNode root = new TreeNode(tmp);
        int mid = 0;
        for(int i = 0; i < inorder.length; i++){
            if(tmp == inorder[i]){
                mid = i;
                break;
            }
        }
        int[] l1 = Arrays.copyOfRange(inorder, 0, mid);
        int[] r1 = Arrays.copyOfRange(postorder, 0, mid);
        root.left = buildTree(l1, r1);
        int[] l2 = Arrays.copyOfRange(inorder, mid+1, inorder.length);
        int[] r2 = Arrays.copyOfRange(postorder, mid, postorder.length-1);
        root.right = buildTree(l2, r2);
        return root;
    }
}

 

五、 二分法

二分查找的条件,复杂度。返回目标元素的位置,不存在返回-1。

整数的二分查找,如果元素类型任意 [C++而言] 。

旋转数组的二分查找。

假设要查找的元素为key,则拢共有以下6种查找情况,第一个=key的元素,第一个>=key的元素,第一个>key的元素;最后一个=key的元素,最后一个<=key的元素,最后一个<key的元素。

二分查找的条件是数组/列表中的元素具备某种顺序,查找的时间复杂度为O(logN),普通的二分查找(递归/非递归)模板如下,其中查找区间为闭区间[L, R]。其中可以有多个等于key的元素,但只需返回任意一个位置。[ LintCode 457 ]

迭代版二分查找模板如下:

public class Solution {
    public int biSearch(int[] arr, int key) {
        int L = 0, R = arr.length - 1;
        while(L <= R){
            int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
            if(arr[mid] > key) R = mid - 1;
            else if(arr[mid] < key) L = mid + 1;
            else return mid;
        }
        return -1;
    }
}

递归版二分查找模板如下:

public class Solution {
    private int biSearch(int[] arr, int key, int L, int R){
        if(L > R) return -1;
        int mid = L + (R-L) / 2;
        if(arr[mid] > key) return biSearch(arr, key, L, mid - 1);
        else if(arr[mid] < key) return biSearch(arr, key, mid + 1, R);
        else return mid;
    } 
    public int findPosition(int[] nums, int target) {
        return biSearch(nums, target, 0, nums.length - 1);
    }
}

其他形式的二分法可以参考:https://github.com/ZXZxin/ZXBlog/blob/master/%E5%88%B7%E9%A2%98/InterviewAlgorithm.md

我这里也录入于此,以供参考。

第一个=key的元素的下标:
public int firstEqual(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] >= key) R = mid - 1;
        else L = mid + 1;
    }
    if(L < arr.length && arr[L] == key) return L;
    return -1;
}

第一个>=key的元素的下标:
public int firstLargeEqual(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] >= key) R = mid - 1;
        else L = mid + 1;
    }
    return L;
}

第一个>key的元素的下标:
public int firstLarge(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] > key) R = mid - 1;
        else L = mid + 1;
    }
    return L;
}

最后一个=key的元素的下标,不存在返回-1:
public int lastEqual(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] <= key) L = mid + 1;
        else R = mid - 1;
    }
    if(R >= 0 && arr[R] == key) return R;
    return -1;
}

最后一个<=key的元素的下标:
public int lastSmallEqual(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] <= key) L = mid + 1;
        else R = mid - 1;
    }
    return R;
}

最后一个<key的元素的下标:
public int lastSmall(int[] arr, int key) {
    int L = 0, R = arr.length - 1;
    while(L <= R){
        int mid = L + (R-L) / 2;    // 防止直接相加后的溢出
        if(arr[mid] < key) L = mid + 1;
        else R = mid - 1;
    }
    return R;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值