日撸java之基本数据结构

这里是java的自学,有问题也请提出哦。

目录

前言

一、栈及其栈的应用(括号匹配)

1.顺序表实现栈

1.1基本属性

1.2入栈操作

1.3出栈操作

1.4括号匹配

 二、递归

1.从1加到N

2.斐波那契数列

三、链队列

四、循环队列

五、字符串匹配


前言

由于准备竞赛的原因,之前也是停了一个星期的学习。

一、栈及其栈的应用(括号匹配)

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈的特点是:先进后出、后进先出,只能在栈顶进行插入和删除操作。

1.顺序表实现栈

 1.1基本属性

/**
	 * The depth.
	 */
	public static final int MAX_DEPTH = 10;

	/**
	 * The actual depth.
	 */
	int depth;

	/**
	 * The data
	 */
	char[] data;

	/**
	 * Construct an empty char stack.
	 */
	public CharStack() {
		depth = 0;
		data = new char[MAX_DEPTH];
	}// of the frist constructor

	/**
	 * ******************** verrides the method claimed in Object, the superclass of
	 * any class. ********************
	 */
	public String toString() {
		String resultString = "";
		for (int i = 0; i < depth; i++) {
			resultString += data[i];
		} // Of for i

		return resultString;
	}// Of toString

我们使用MAX_LENGTH表示栈的最大深度,在这里我们定义的数组的最右端是我们的栈顶,因为我们的插入和删除操作都是在栈顶进行操作的,这里我们就在数组的右端进行插入和删除,而我们重写的toString方法是从左到右进行输出的,即表示从栈底向栈顶的打印过程。

1.2入栈操作

/**
	 ****************
	 * Push an element
	 *
	 * @param paraChar The given char.
	 * @return Success or Not
	 ****************
	 */
	public boolean push(char paraChar) {
		if (depth == MAX_DEPTH) {
			System.out.println("Stack full.");
			return false;
		} // of if
		data[depth] = paraChar;
		depth++;

		return true;
	}// of push

在插入操作时我们需要进行栈是否已经满的判定,来保证栈的健壮性。栈顶的指针是指向栈的最后一位元素的后面一位,在插入元素时,我们先将元素放入栈中,然后将栈顶指针后移。

1.3出栈操作

/**
	 ****************
	 * Pop an element.
	 *
	 * @return The poped char.
	 ****************
	 */
	public char pop() {
		if (depth == 0) {
			System.out.println("Stack empty.");
			return '\0';
		} // of if
		char resultChar = data[depth - 1];
		depth--;
		return resultChar;
	}// of pop

元素在出栈时,我们先进行栈为空的判定,栈如果都为空了肯定没有元素供我们取了。由于我们的栈顶指针是指向最后一位元素的后一位,在元素出栈时,我们取元素时取得是depth-1的元素。

1.4括号匹配

/**
	 ****************
	 * Is the bracket Matching?
	 *
	 * @param paraString The given Expression.
	 * 
	 * @return Match or not.
	 ****************
	 */
	public static boolean bracketMatching(String paraString) {
		// Step 1.Initialize the stack through pushing a '#' at the bottom
		CharStack tempCharStack = new CharStack();
		tempCharStack.push('#');
		char tempChar, tempPopedChar;

		/// Step 2. Process the string. For a string, length() is a method
		// instead of a member variable.

		for (int i = 0; i < paraString.length(); i++) {
			tempChar = paraString.charAt(i);
			switch (tempChar) {
			case '(':
			case '[':
			case '{':
				tempCharStack.push(tempChar);
				break;
			case ')':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '(') {
					return false;
				} // of if
				break;
			case ']':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '[') {
					return false;
				} // of if
				break;
			case '}':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '{') {
					return false;
				} // of if
				break;
			default:

			}// of switch
		} // of for i

		tempPopedChar = tempCharStack.pop();
		if (tempPopedChar != '#') {
			return false;
		} // of if

		return true;
	}// of bracketMatching

我们在匹配到左括号时就将匹配到的左括号放入栈中,在右括号来时,我们和栈顶的括号进行匹配,如果能够组成一队,我们就将此时的栈顶元素进行出栈。在这里我们用{ [](()) }进行一部分的演示

在最后我们的栈里面的元素只有'#'时代表我们的括号匹配成功。 

总的代码

package day_14;

/**
 * 
 * This is the fourteenth code.
 *
 * @author Juncai Chen 3039808589@qq.com.
 *
 */
public class CharStack {
	/**
	 * The depth.
	 */
	public static final int MAX_DEPTH = 10;

	/**
	 * The actual depth.
	 */
	int depth;

	/**
	 * The data
	 */
	char[] data;

	/**
	 * Construct an empty char stack.
	 */
	public CharStack() {
		depth = 0;
		data = new char[MAX_DEPTH];
	}// of the frist constructor

	/**
	 * ******************** verrides the method claimed in Object, the superclass of
	 * any class. ********************
	 */
	public String toString() {
		String resultString = "";
		for (int i = 0; i < depth; i++) {
			resultString += data[i];
		} // Of for i

		return resultString;
	}// Of toString

	/**
	 ****************
	 * Push an element
	 *
	 * @param paraChar The given char.
	 * @return Success or Not
	 ****************
	 */
	public boolean push(char paraChar) {
		if (depth == MAX_DEPTH) {
			System.out.println("Stack full.");
			return false;
		} // of if
		data[depth] = paraChar;
		depth++;

		return true;
	}// of push

	/**
	 ****************
	 * Pop an element.
	 *
	 * @return The poped char.
	 ****************
	 */
	public char pop() {
		if (depth == 0) {
			System.out.println("Stack empty.");
			return '\0';
		} // of if
		char resultChar = data[depth - 1];
		depth--;
		return resultChar;
	}// of pop

	/**
	 ****************
	 * Is the bracket Matching?
	 *
	 * @param paraString The given Expression.
	 * 
	 * @return Match or not.
	 ****************
	 */
	public static boolean bracketMatching(String paraString) {
		// Step 1.Initialize the stack through pushing a '#' at the bottom
		CharStack tempCharStack = new CharStack();
		tempCharStack.push('#');
		char tempChar, tempPopedChar;

		/// Step 2. Process the string. For a string, length() is a method
		// instead of a member variable.

		for (int i = 0; i < paraString.length(); i++) {
			tempChar = paraString.charAt(i);
			switch (tempChar) {
			case '(':
			case '[':
			case '{':
				tempCharStack.push(tempChar);
				break;
			case ')':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '(') {
					return false;
				} // of if
				break;
			case ']':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '[') {
					return false;
				} // of if
				break;
			case '}':
				tempPopedChar = tempCharStack.pop();
				if (tempPopedChar != '{') {
					return false;
				} // of if
				break;
			default:

			}// of switch
		} // of for i

		tempPopedChar = tempCharStack.pop();
		if (tempPopedChar != '#') {
			return false;
		} // of if

		return true;
	}// of bracketMatching

	/**
	 ****************
	 * The entrance of the program.
	 *
	 * @param args Not uesd now.
	 ****************
	 */
	public static void main(String[] args) {
		CharStack tempCharStack = new CharStack();

		for (char ch = 'a'; ch < 'm'; ch++) {
			tempCharStack.push(ch);
			System.out.println("The current stack is:" + tempCharStack.toString());
		} // of for ch

		char tempChar;
		for (int i = 0; i < 12; i++) {
			tempChar = tempCharStack.pop();
			System.out.println("Poped: " + tempChar);
			System.out.println("The current stack is: " + tempCharStack.toString());
		} // Of for i

		boolean tempMatch;
		String tempExpression = "[2 + (1 - 3)] * 4";
		tempMatch = bracketMatching(tempExpression);

		System.out.println("Is the expression " + tempExpression + " bracket matching? " + tempMatch);
		tempExpression = "( ) )";
		tempMatch = bracketMatching(tempExpression);

		System.out.println("Is the expression " + tempExpression + " bracket matching? " + tempMatch);

		tempExpression = "()()(())";
		tempMatch = bracketMatching(tempExpression);

		System.out.println("Is the expression " + tempExpression + " bracket matching? " + tempMatch);

		tempExpression = ")(";
		tempMatch = bracketMatching(tempExpression);

		System.out.println("Is the expression " + tempExpression + " bracket matching? " + tempMatch);
		tempExpression = "{ [](()) }";
		tempMatch = bracketMatching(tempExpression);

		System.out.println("Is the expression " + tempExpression + " bracket matching? " + tempMatch);
	}// of main

}// of class CharStack

执行结果:

 

 二、递归

递归就是在执行过程中自己调用自己。从一个我们很小就听过的故事来理解一下递归:             

从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……’”

我们通过两个例子来更好的理解递归:

1.从1加到N

/**
	 ****************
	 *Sum to N. No loop, however a stack is used.
	 *
	 * @param paraN The given value.
	 * @return The sum.
	 ****************
	 */
	public static int sumToN(int paraN) {
		if(paraN<=0) {
			return 0;
		}//of if
		
		return paraN+sumToN(paraN-1);
	}//of sumToN

2.斐波那契数列

/**
	 ****************
	 *Fibonacci suquence.
	 *
	 * @param paraN The given value.
	 * 
	 * @return The number we need.
	 ****************
	 */
	public static int fibonacci(int paraN) {
		if (paraN <= 0) {
			//Negative values are invalid. Index 0 corresponds to the first element 0.
			return 0;
		} if (paraN == 1) {
			//Basis.
			return 1;
		}//Of if
		
		return fibonacci(paraN - 1) + fibonacci(paraN - 2);
	}

斐波那契数列也是在让我们理解递归的过程中很好的例子,如果对递归还不太理解,可以边画图边去理解递归。

我们知道递归必须具备两个条件,一个是调用自己,一个是有终止条件。这两个条件必须同时具备,且一个都不能少。并且终止条件必须是在递归最开始的地方,也就是下面这样

public void recursion(参数0) {
    if (终止条件) {
        return;
    }
    recursion(参数1);
}

注意:

千万不能将终止条件写在后面,就像这样

public void recursion(参数0) {
    recursion(参数1);
    if (终止条件) {
        return;
    }
}

这样的话递归就会永远也退不出来,会出现堆栈溢出异常(StackOverflowError)。

总的代码:

package day_15;

/**
 * 
 * This is the fifteenth code.
 * 
 * @author Juncai Chen 3039808589@qq.com.
 *
 */
public class Recursion {
	/**
	 ****************
	 * Sum to N. No loop, however a stack is used.
	 *
	 * @param paraN The given value.
	 * @return The sum.
	 ****************
	 */
	public static int sumToN(int paraN) {
		if (paraN <= 0) {
			return 0;
		} // of if

		return paraN + sumToN(paraN - 1);
	}// of sumToN

	/**
	 ****************
	 * Fibonacci suquence.
	 *
	 * @param paraN The given value.
	 * 
	 * @return The number we need.
	 ****************
	 */
	public static int fibonacci(int paraN) {
		if (paraN <= 0) {
			// Negative values are invalid. Index 0 corresponds to the first element 0.
			return 0;
		}
		if (paraN == 1) {
			// Basis.
			return 1;
		} // Of if

		return fibonacci(paraN - 1) + fibonacci(paraN - 2);
	}

	/**
	 ****************
	 * The entrance of the program.
	 *
	 * @param args Not used now.
	 ****************
	 */
	public static void main(String[] args) {
		int tempValue = 5;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));
		tempValue = -1;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));

		for (int i = 0; i < 10; i++) {
			System.out.println("Fibonacci " + i + ": " + fibonacci(i));
		} // Of for i1
	}
}

执行的结果:

三、链队列

链表是很好理解的,就想成我们生活中的排队即可,我们生活中排队就是先排的能够先出去,在我们基本数据结构队列中也是先进先出。在队列中,我们是在队首进行删除操作,在队尾进行插入操作。

入队操作:

/**
	 *********************
	 * Enqueue.
	 * 
	 * @param paraValue The value of the new node.
	 *********************
	 */
	public void enqueue(int paraValue) {
		Node tempNode = new Node(paraValue);
		tail.next = tempNode;
		tail = tempNode;
	}// Of enqueue

 出队操作:

/**
	 *********************
	 * Dequeue.
	 * 
	 * @return The value at the header.
	 *********************
	 */
	public int dequeue() {
		if (header == tail) {
			System.out.println("No element in the queue");
			return -1;
		} // Of if

		int resultValue = header.next.data;

		header.next = header.next.next;

		// The queue becomes empty.
		if (header.next == null) {
			tail = header;
		} // Of if

		return resultValue;
	}// Of dequeue

代码总览:

package day_16;


/**
 * 
 * This is the sixteenth code.
 *
 * @author Juncai Chen 3039808589@qq.com.
 *
 */
public class LinkedQueue {

	/**
	 * An inner class.
	 */
	class Node {
		/**
		 * The data.
		 */
		int data;

		/**
		 * The reference to the next node.
		 */
		Node next;

		/**
		 ******************* 
		 * The constructor.
		 * 
		 * @param paraValue The data.
		 ******************* 
		 */
		public Node(int paraValue) {
			data = paraValue;
			next = null;
		}// Of the constructor
	}// Of class Node

	/**
	 * The header of the queue.
	 */
	Node header;

	/**
	 * The tail of the queue.
	 */
	Node tail;

	/**
	 *********************
	 * Construct an empty sequential list.
	 *********************
	 */
	public LinkedQueue() {
		header = new Node(-1);
		// header.next = null;

		tail = header;
	}// Of the first constructor

	/**
	 *********************
	 * Enqueue.
	 * 
	 * @param paraValue The value of the new node.
	 *********************
	 */
	public void enqueue(int paraValue) {
		Node tempNode = new Node(paraValue);
		tail.next = tempNode;
		tail = tempNode;
	}// Of enqueue

	/**
	 *********************
	 * Dequeue.
	 * 
	 * @return The value at the header.
	 *********************
	 */
	public int dequeue() {
		if (header == tail) {
			System.out.println("No element in the queue");
			return -1;
		} // Of if

		int resultValue = header.next.data;

		header.next = header.next.next;

		// The queue becomes empty.
		if (header.next == null) {
			tail = header;
		} // Of if

		return resultValue;
	}// Of dequeue

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "";

		if (header.next == null) {
			return "empty";
		} // Of if

		Node tempNode = header.next;
		while (tempNode != null) {
			resultString += tempNode.data + ", ";
			tempNode = tempNode.next;
		} // Of while

		return resultString;
	}// Of toString

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		LinkedQueue tempQueue = new LinkedQueue();
		System.out.println("Initialized, the list is: " + tempQueue.toString());

		for (int i = 0; i < 5; i++) {
			tempQueue.enqueue(i + 1);
		} // Of for i
		System.out.println("Enqueue, the queue is: " + tempQueue.toString());

		tempQueue.dequeue();
		System.out.println("Dequeue, the queue is: " + tempQueue.toString());

		int tempValue;
		for (int i = 0; i < 5; i++) {
			tempValue = tempQueue.dequeue();
			System.out.println("Looped delete " + tempValue + ", the new queue is: " + tempQueue.toString());
		} // Of for i

		for (int i = 0; i < 3; i++) {
			tempQueue.enqueue(i + 10);
		} // Of for i
		System.out.println("Enqueue, the queue is: " + tempQueue.toString());
	}// Of main
}// Of class LinkedQueue

执行的结果:

四、循环队列

在普通的队列中,如下图1,2出队后,此时队列中还有空余的位置,但是这个时候已经不能在有数据进行进队操作了,这个时候就造成了假溢出,为了解决假溢出的问题,我们就引入了循环队列。

 循环队列的示意图:


 

入队操作:

/**
	 *********************
	 * Enqueue.
	 * 
	 * @param paraValue The value of the new node.
	 *********************
	 */
	public void enqueue(int paraValue) {
		if ((tail + 1) % TOTAL_SPACE == head) {
			System.out.println("Queue full.");
			return;
		} // Of if

		data[tail % TOTAL_SPACE] = paraValue;
		tail++;
	}// Of enqueue

如何判断循环队列已经满了?

由于尾元素和首元素之前有一个空位置,我们判断队满的操作是:(tail + 1) % TOTAL_SPACE == head

出队操作:

/**
	 *********************
	 * Dequeue.
	 * 
	 * @return The value at the head.
	 *********************
	 */
	public int dequeue() {
		if (head == tail) {
			System.out.println("No element in the queue");
			return -1;
		} // Of if

		int resultValue = data[head % TOTAL_SPACE];

		head++;

		return resultValue;
	}// Of dequeue

总的代码:

package day_17;

/**
 *This is the seventeenth code.
 * 
 * @author Juncai Chen 3039808589@qq.com.
 *
 */
public class CircleIntQueue {

	/**
	 * The total space. One space can never be used.
	 */
	public static final int TOTAL_SPACE = 10;

	/**
	 * The data.
	 */
	int[] data;

	/**
	 * The index for calculating the head. The actual head is head % TOTAL_SPACE.
	 */
	int head;

	/**
	 * The index for calculating the tail.
	 */
	int tail;

	/**
	 ******************* 
	 * The constructor
	 ******************* 
	 */
	public CircleIntQueue() {
		data = new int[TOTAL_SPACE];
		head = 0;
		tail = 0;
	}// Of the first constructor

	/**
	 *********************
	 * Enqueue.
	 * 
	 * @param paraValue The value of the new node.
	 *********************
	 */
	public void enqueue(int paraValue) {
		if ((tail + 1) % TOTAL_SPACE == head) {
			System.out.println("Queue full.");
			return;
		} // Of if

		data[tail % TOTAL_SPACE] = paraValue;
		tail++;
	}// Of enqueue

	/**
	 *********************
	 * Dequeue.
	 * 
	 * @return The value at the head.
	 *********************
	 */
	public int dequeue() {
		if (head == tail) {
			System.out.println("No element in the queue");
			return -1;
		} // Of if

		int resultValue = data[head % TOTAL_SPACE];

		head++;

		return resultValue;
	}// Of dequeue

	/**
	 *********************
	 * Overrides the method claimed in Object, the superclass of any class.
	 *********************
	 */
	public String toString() {
		String resultString = "";

		if (head == tail) {
			return "empty";
		} // Of if

		for (int i = head; i < tail; i++) {
			resultString += data[i % TOTAL_SPACE] + ", ";
		} // Of for i

		return resultString;
	}// Of toString

	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String args[]) {
		CircleIntQueue tempQueue = new CircleIntQueue();
		System.out.println("Initialized, the list is: " + tempQueue.toString());

		for (int i = 0; i < 5; i++) {
			tempQueue.enqueue(i + 1);
		} // Of for i
		System.out.println("Enqueue, the queue is: " + tempQueue.toString());

		int tempValue = tempQueue.dequeue();
		System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());

		for (int i = 0; i < 6; i++) {
			tempQueue.enqueue(i + 10);
			System.out.println("Enqueue, the queue is: " + tempQueue.toString());
		} // Of for i

		for (int i = 0; i < 3; i++) {
			tempValue = tempQueue.dequeue();
			System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());
		} // Of for i

		for (int i = 0; i < 6; i++) {
			tempQueue.enqueue(i + 100);
			System.out.println("Enqueue, the queue is: " + tempQueue.toString());
		} // Of for i
	}// Of main

}// Of CircleIntQueue

执行结果:

五、字符串匹配

在这里我们进行字符串匹配的思路是从我们的主字符串中选出和子字符串的长度相等的字符串出来,我们选出的字符串是连续的,然后在和子字符串进行逐一比较。

 代码如下:

package day_19;

/**
 *This is the nineteenth code. 
 *
 * @author Juncai Chen 3039808589@qq.com.
 *
 */
public class MyString {
	
	/**
	 * The maximal length.
	 */
	public static final int MAX_LENGTH=10;
	
	/**
	 * The actual length.
	 */
	int length;
	
	/**
	 * The actual data.
	 */
	char[] data;
	
	/**
	 * **************
	 * Construct an empty char array.
	 * **************
	 */
	public MyString() {
		length=0;
		data=new char[MAX_LENGTH];
	}//of the first constructor
	
	
	public MyString(String paraString) {
		data=new char[MAX_LENGTH];
		length=paraString.length();
		
		//Copy data.
		for (int i = 0; i < length; i++) {
			data[i]=paraString.charAt(i);
		}//of for i
	}//of the second constructor
	
	/**
	 * *************************
	 * Overrides the method claimed in Object, the superclass of any class.
	 * *************************
	 */
	public String toString() {
		String resultString="";
		
		for (int i = 0; i < length; i++) {
			resultString+=data[i];
		}//of for i
		
		return resultString;
	}//of to String
	
	/**
	 ****************
	 *Locate the position of substring.
	 *
	 * @param paraMyString The given substring.
	 * @return The frist position. -1 for no matching.
	 ****************
	 */
	public int locate(MyString paraMyString) {
		boolean tempMatch=false;
		for (int i = 0; i < length-paraMyString.length+1; i++) {
			//Initialize 
			tempMatch=true;
			for (int j = 0; j < paraMyString.length; j++) {
				if(data[i+j]!=paraMyString.data[j]) {
					tempMatch=false;
					break;
				}//of if
			}//of for j
			
			if(tempMatch) {
				return i;
			}//of if
		}//of for i
		return -1;
	}//of locate 
	
	/**
	 ****************
	 *Get a substring
	 *
	 * @param paraStartPosition The start position in the original string.
	 * @param paraLength The length of the new string.
	 * 
	 * @return The first position. -1 for no matching.
	 ****************
	 */
	public MyString substring(int paraStartPosition, int paraLength) {
		if (paraStartPosition + paraLength > length) {
			System.out.println("The bound is exceeded.");
			return null;
		} // Of if

		MyString resultMyString = new MyString();
		resultMyString.length = paraLength;
		for (int i = 0; i < paraLength; i++) {
			resultMyString.data[i] = data[paraStartPosition + i];
		} // Of for i

		return resultMyString;
	}// Of substring
	
	/**
	 ****************
	 *The entrance of the program.
	 *
	 * @param args Not used now.
	 ****************
	 */
	public static void main(String[] args) {
		MyString tempFirstString = new MyString("I like ik.");
		MyString tempSecondString = new MyString("ik");
		int tempPosition = tempFirstString.locate(tempSecondString);
		System.out.println("The position of \"" + tempSecondString + "\" in \"" + tempFirstString
				+ "\" is: " + tempPosition);
 
		MyString tempThirdString = new MyString("ki");
		tempPosition = tempFirstString.locate(tempThirdString);
		System.out.println(
				"The position of \"" + tempThirdString + "\" in \"" + tempFirstString + "\" is: " + tempPosition);
 
		tempThirdString = tempFirstString.substring(1, 2);
		System.out.println("The substring is: \"" + tempThirdString + "\"");
 
		tempThirdString = tempFirstString.substring(5, 5);
		System.out.println("The substring is: \"" + tempThirdString + "\"");
 
		tempThirdString = tempFirstString.substring(5, 6);
		System.out.println("The substring is: \"" + tempThirdString + "\"");

	}//of main
}//of class MyString

执行的结果:

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值