11-20天,线性数据结构
原文:日撸代码300行(11-20天,线性数据结构)_minfanphd的博客-CSDN博客
11.顺序表(一)
package day20;
/**
* Day 11: 顺序表
*
* @author pzf
*/
public class SequentialList {
final int MAX_LENGTH = 5;
int length;
int[] data;
/**
* 无参构造方法
*/
public SequentialList() {
length = 0;
data = new int[MAX_LENGTH];
}// Of SequentialList
/**
* 带参数的构造方法
*
* @param paraLength 数组
*/
public SequentialList(int[] paraLength) {
data = new int[MAX_LENGTH];
// 参数数组长度是否超出MAX_LENGTH
length = paraLength.length > MAX_LENGTH ? MAX_LENGTH : paraLength.length;
for (int i = 0; i < length; i++) {
data[i] = paraLength[i];
}// Of for
}// Of SequentialList with parameter
/**
* 重写toString
*
* @return 字符串
*/
public String toString() {
String res = ""; // 空串
if (length == 0) {
return "Empty";
}// Of if
for (int i = 0; i < length - 1; i++) {
res += data[i] + ", ";
}// Of for
res += data[length - 1];
return res;
}// Of toString
/**
* 重置线性表
*/
public void reset() {
length = 0;
}// Of reset
/**
* main 测试用
*
* @param args
*/
public static void main(String[] args) {
int[] testArr = {1, 2, 3, 5, 6, 9}; // 超出MAX_LENGTH
int[] testArr2 = {1, 3}; // 正常
SequentialList seqList1 = new SequentialList(testArr);
SequentialList seqList2 = new SequentialList(testArr2);
System.out.println("SeqList1:" + seqList1);
System.out.println("SeqList2:" + seqList2);
seqList1.reset();
System.out.println("SeqList1 after reset:" + seqList1);
}// Of main
}// Of class SequentialList
效仿闵老师代码写了一遍。
做了哪些事:
1.定义最大长度(这里偷懒定义为5,方便测试),定义属性:长度、值
2.构造方法,无参+有参。顺便检查了长度是否超出最大长度,超出的截取前面。
3.重写toString,格式为 “1, 2, 3”
4.reset方法,清空线性表
可以改进的地方:
1.代码规范,变量的命名感觉不够规范
2.对常用内置类不熟悉,经提醒,一些地方实现方式可以调用已有的函数。如:为了防止构造方法中参数数组长度大于最大长度时报错,用三目运算符简单处理,其实可以Math.min();构造方法内复制数据用arrayCopy.
3.toString里用StringBuffer,免得一直在那+++拼接
12.顺序表(二)
/**
* 查找给定元素所处的位置. 找不到就返回 -1.
*
* @param paraValue 要查找的值
* @return 位置
*/
public int indexOf(int paraValue) {
int resPosition = -1;
for (int i = 0; i < length; i++) {
if (paraValue == data[i]) {
resPosition = i;
break;
}// Of if
}// Of for
return resPosition;
}// Of indexOf
/**
* 在给定位置增加元素. 如果线性表已满, 或位置不在已有位置范围之内, 就拒绝增加.
* 该位置可以是在最后一个元素之后一个.
*
* @param paraValue 插入值
* @param paraPosition 位置,从0开始
* @return 是否插入成功
*/
public boolean insert(int paraValue, int paraPosition) {
//1.位置是否合法
if (paraPosition > length || paraPosition < 0) {
System.out.println("Invalid input.");
return false;
}// Of if
//2.线性表是否还有空位
if (length == MAX_LENGTH) {
System.out.println("List full.");
return false;
}// Of if
//3.正确插入,后移后面元素
for (int i = length; i > paraPosition; i--) {
data[i] = data[i - 1];
}// Of for
data[paraPosition] = paraValue;
length++;
return true;
}//Of insert
/**
* 删除定定位置的元素. 要处理给定位置不合法的情况. 该位置必须是已经有数据的.
*
* @param paraPosition 指定元素的位置,从0开始
* @return 是否成功
*/
public boolean delete(int paraPosition) {
//1.检查位置是否合法
if (paraPosition > length || paraPosition < 0) {
System.out.println("Invalid input");
return false;
}// Of if
//2.正常删除
for (int i = paraPosition ; i < length - 1; i++) {
data[i] = data[i + 1];
}// Of for
length--;
return true;
}// Of delete
在day11代码基础上新增3个新方法
这里如果从用户出发,那么自然插入、删除等操作的下标应该从1开始,代码中就要把位置的参数-1,但是为了理解和敲代码方便,还是从0开始了
13.链表
package day20;
/**
* Day 13: 链表
*
* @author pzf
*/
public class LinkedList {
/**
* 节点类
*/
class Node{
int data; // 值
Node next; // 指针
/**
* 构造方法
*
* @param paraValue 值
*/
public Node(int paraValue) {
data = paraValue;
next = null;
}// Of the constructor
}//Of class Node
Node headNode;
/**
* 构造方法 头结点赋值 -1
*/
public LinkedList(){
headNode = new Node(-1);
}// Of the constructor
/**
* 重写toString
*
* @return 字符串
*/
public String toString(){
String res = "";
//1.判空
if(headNode.next == null){
return "Empty List";
}// Of if
//2.正常处理
Node tempNode = headNode.next;
while(tempNode.next!=null){
res += tempNode.data + " -> ";
tempNode = tempNode.next;
}// Of while
res += tempNode.data;
return res;
}// Of toString
/**
* 在指定位置插入
*
* @param paraPosition 指定位置
* @param paraValue 插入值
* @return true:成功 false:失败
*/
public boolean insert(int paraPosition, int paraValue){
// 1.找位置+判断位置是否合法
Node tempNode = headNode;
for (int i = 0; i < paraPosition; i++) {
if(tempNode.next == null){
System.out.println("Invalid input");
return false;
}// Of if
tempNode = tempNode.next;
}// Of for
// 2.插入
Node insertNode = new Node(paraValue);
insertNode.next = tempNode.next;
tempNode.next = insertNode;
return true;
}// Of insert
/**
* 重置链表
*/
public void reset(){
headNode.next = null;
}// Of reset
/**
* 删除指定位置的节点
*
* @param paraPosition 指定位置
* @return true:成功删除 false:删除失败
*/
public boolean delete(int paraPosition){
// 1. 判空
if(headNode.next == null){
System.out.println("Cannot delete element from an empty list.");
return false;
}// Of if
Node tempPreNode = headNode;
Node tempNode = headNode.next;
// 2.判断合法性,定位
for (int i = 0; i < paraPosition; i++) {
if(tempNode.next == null){
System.out.println("Invalid input");
return false;
}// Of if
tempNode = tempNode.next;
tempPreNode = tempPreNode.next;
}// Of for
// 3.执行删除
tempPreNode.next = tempNode.next;
return true;
}// Of delete
//定位
/**
* 返回参数值的位置,如果没有,返回 -1
*
* @param paraValue 要查找的值
* @return int 找到:位置 没找到:-1
*/
public int locate(int paraValue){
Node tempNode = headNode.next;
int res = 0;
while(tempNode!=null){
if(paraValue == tempNode.data){
return res;
}// Of if
res++;
tempNode = tempNode.next;
}// Of while
return -1;
}// Of locate
/**
* main 测试用
*
* @param args
*/
public static void main(String[] args) {
LinkedList list = new LinkedList();
for (int i = 0; i < 5; i++) {
list.insert(0,i);
}
System.out.println(list);
System.out.println(list.locate(0));
for (int i = 4; i >=0; i--) {
list.delete(i);
System.out.println(list);
}
}// Of main
}// Of class LinkedList
测试结果:
做了哪些事:
1.定义节点内部类,包含值和指针2个属性
2.构造方法
3.重写toString
4.增删查、重置链表的方法
反思:
Java注意要空指针的异常,增删查在遍历指针的时候一不小心就会有少写next的情况,会造成无限循环或者空指针报错
备考过程中一直看的C,现在回到Java有点不适应了,总在想参数是不是应该把链表加进去,构造方法不看老师代码都写不出来
14.栈
package day20;
/**
* Day14: Stack
*
* @author pzf
*/
public class CharStack {
//attributes
final int MAX_DEPTH = 30;
int depth;
char[] data;
/**
* Constructor
*/
public CharStack() {
depth = 0;
data = new char[MAX_DEPTH];
}// Of constructor
/**
* Push
*
* @param paraValue The given char
* @return Success:true fail: false
*/
public boolean push(char paraValue) {
//1. full ?
if (depth == MAX_DEPTH) {
System.out.println("Full");
return false;
}// Of if
//2. push
data[depth++] = paraValue;
return true;
}// Of push
/**
* Pop
*
* @return The element on the top of stack
*/
public char pop() {
// 1. is empty stack ?
if (depth == 0) {
System.out.println("Empty");
return '\0';
}// Of if
// 2. pop
return data[--depth];
}// Of pop
/**
* Reset stack
*/
public void reset() {
depth = 0;
}// Of reset
/**
* Override toString
*
* @return Format stack
*/
public String toString() {
String res = "";
for (int i = 0; i < depth; i++) {
res += data[i];
}// Of for
return res;
}// Of toString
/**
* main
*
* @param args
*/
public static void main(String[] args) {
CharStack tempStack = new CharStack();
for (int i = 0; i < 10; i++) {
tempStack.push((char)(i+97));
}// Of for
System.out.println(tempStack);
System.out.println(tempStack.pop());
tempStack.pop();
tempStack.pop();
System.out.println(tempStack);
tempStack.reset();
System.out.println(tempStack);
}// Of main
}// Of class CharStack
运行截图:
做了哪些事:
1.定义类,属性有常量:最大深度,变量:深度、栈顶元素
2.构造方法
3.push and pop
3.reset、重写toString
反思:英文的注释好难写,水平不够,想要简短地表达意思比写一大段话更难
15.括号匹配(栈的应用)
package day20;
import day20.CharStack;
/**
* Day 15: bracket matching
*
* @author pzf
*/
public class StackTest {
/**
* Is the bracket matching?
*
* @param paraString The given parameter
* @return True or false
*/
public static boolean bracketMatching(String paraString) {
CharStack tempStack = new CharStack();
for (int i = 0; i < paraString.length(); i++) {
char tempChar = paraString.charAt(i);
switch (tempChar) {
case '(':
case '[':
case '{':
tempStack.push(tempChar);
break;
case ')':
if (tempStack.pop() != '(') {
return false;
}
break;
case ']':
if (tempStack.pop() != '[') {
return false;
}
break;
case '}':
if (tempStack.pop() != '{') {
return false;
}
break;
default:
// Do noting
}// Of switch
}// Of for
// Of if
return tempStack.depth == 0;
}// Of bracketMatching
/**
* main,test
*
* @param args
*/
public static void main(String[] args) {
String s1 = "( ) )";
String s2 = "[2 + (1 - 3)] * 4";
String s3 = "()()(())";
String s4 = "({}[])";
System.out.println(bracketMatching(s1));
System.out.println(bracketMatching(s2));
System.out.println(bracketMatching(s3));
System.out.println(bracketMatching(s4));
}// Of main
}// Of class StackTest
为了简洁,新建了类导入了前一天的代码。
由于只有括号进栈,所以用最后栈是否为空表示返回结果。
[一开始漏掉了选择语句里遇到不匹配的情况后的break,以为既然已经return了那么break是多余的,忽略了正确匹配的情况。。]
运行结果:
(尴尬的地方:这里用pop方法遇到空栈时会打印一个Empty)
16.递归
package day20;
/**
* Day16: Recursion
*
* @author pzf
*/
public class Recursion {
/**
* Sum to N
*
* @param paraN The given value.
* @return The sum.
*/
public static int sum(int paraN) {
if (paraN <= 0) {
return 0;
}// Of if
return sum(paraN - 1) + paraN;
}// Of sum
/**
* Fibonacci sequence.
*
* @param paraN The given value.
* @return The paraNth number in fibonacci sequence.
*/
public static int fib(int paraN){
if(paraN <= 0){
return 0;
}// Of if
if(paraN == 1 || paraN == 2){
return 1;
}// Of if
return fib(paraN-1) + fib(paraN - 2);
}// Of fib
/**
* main
*
* @param args
*/
public static void main(String[] args) {
System.out.println(sum(10));
System.out.println(fib(10));
}// Of main
}// Of class Recursion
17.链队列
package day20;
/**
* Day17: LinkedQueue
*
* @author pzf
*/
public class LinkedQueue {
/**
* Inner class:Node
*/
private class Node {
// attributes
public int data;
public Node next;
/**
* contractor
* @param paraValue
*/
public Node(int paraValue) {
data = paraValue;
next = null;
}// Of contractor
}// Of class Node
//attributes
Node headNode;
Node tailNode;
/**
* contractor
*/
public LinkedQueue() {
headNode = new Node(-1);
tailNode = headNode;
}// Of contractor
/**
* Enqueue
*
* @param paraValue The given value.
*/
public void enqueue(int paraValue) {
Node tempNode = new Node(paraValue);
tailNode.next = tempNode;
tailNode = tempNode;
}// Of enqueue
/**
* Dequeue
*
* @return The number at the headNode
*/
public int dequeue() {
// empty?
if (headNode == tailNode) {
System.out.println("Empty queue");
return -1;
}// Of if
int res = headNode.next.data;
headNode.next = headNode.next.next;
// if empty
if(headNode.next==null){
tailNode = headNode;
}// Of if
return res;
}// Of dequeue
/**
* Overrides toString
*
* @return format string
*/
public String toString() {
String res = "";
if (headNode.next == null) {
return "empty";
}// Of if
Node tempNode = headNode.next;
while(tempNode.next !=null ){
res += tempNode.data + " -> ";
tempNode = tempNode.next;
}// Of while
res += tempNode.data;
return res;
}// Of toString
/**
* main,test
*
* @param args
*/
public static void main(String[] args) {
LinkedQueue queue = new LinkedQueue();
for (int i = 0; i < 5; i++) {
queue.enqueue(i);
}// Of for
System.out.println(queue);
for (int i = 0; i < 6; i++) {
System.out.println(queue.dequeue());
}// Of for
}// Of main
}// Of class LinkedQueue
运行截图:
18.循环队列
package day20;
/**
* Day 18: CircleQueue
*
* @author pzf
*/
public class CircleQueue {
//attributes
final static int MAX_SPACE = 10;
int[] data;
int head, tail;
/**
* contractor
*/
public CircleQueue() {
data = new int[MAX_SPACE];
head = 0;
tail = 0;
}// Of contractor
/**
* enqueue
*
* @param paraValue The given value.
* @return true of false
*/
public boolean enqueue(int paraValue) {
// 1. full?
if ((tail + 1) % MAX_SPACE == head) {
System.out.println("Full");
return false;
}// Of if
// 2.enqueue
data[tail % MAX_SPACE] = paraValue;
tail = ++tail % MAX_SPACE;
return true;
}// Of enqueue
/**
* dequeue
*
* @return The value of head
*/
public int dequeue() {
// 1. empty?
if (head == tail) {
System.out.println("Empty");
return -1;
}// Of if
// 2. dequeue
int res = data[head % MAX_SPACE];
head = (head + 1) % MAX_SPACE;
return res;
}// Of dequeue
/**
* Overrides toString
*
* @return String
*/
public String toString() {
String res = "";
/*for (int i = head; ; i++) {
if (i % MAX_SPACE == tail) {
break;
}
res += data[i % MAX_SPACE] + ", ";
}*/
int count = head;
while (count % MAX_SPACE != tail) {
res += data[count % MAX_SPACE] + ", ";
count++;
}// Of while
// remove the extra ", "
if(res.length()>0){
res = res.substring(0,res.length()-2);
}// Of if
return res;
}// Of toString
/**
* main,test
*
* @param args
*/
public static void main(String[] args) {
CircleQueue cq = new CircleQueue();
for (int i = 0; i < 10; i++) {
cq.enqueue(i);
}
System.out.println(cq);
for (int i = 0; i < 10; i++) {
cq.dequeue();
}
System.out.println(cq);
for (int i = 0; i < 4; i++) {
cq.enqueue(i + 5);
System.out.println(cq);
}
}// Of main
}// Of class CircleQueue
运行结果:
强迫症,偏要去掉最后的逗号,之前少循环一次然后最后拼接最后一个值的方法在循环队列里有BUG,干脆换个写法。
19.字符串匹配
package day20;
/**
* Day19: MyString
*
* @author pzf
*/
public class MyString {
// attributes
final static int MAX_LENGTH = 10;
int length;
char[] data;
/**
* contractor
*/
public MyString() {
length = 0;
data = new char[MAX_LENGTH];
}// Of contractor
/**
* Contractor with parameter
*
* @param paraString String
*/
public MyString(String paraString) {
data = new char[MAX_LENGTH];
for (int i = 0; i < paraString.length() && i < MAX_LENGTH; i++) {
data[i] = paraString.charAt(i);
length = i + 1;
}// Of for
}// Of contractor with parameter
/**
* Overrides toString
*
* @return String
*/
public String toString() {
String res = "";
for (int i = 0; i < length; i++) {
res += data[i];
}// Of for
return res;
}// Of toString
/**
* Locate the position of a substring.
*
* @param paraString The given substring.
* @return position. -1 for invalid input
*/
public int locate(MyString paraString) {
int paraLength = paraString.length;
if (paraLength == 0 || paraLength > MAX_LENGTH) {
System.out.println("Invalid input");
return -1;
}// Of if
boolean flag;
for (int i = 0; i < length - paraLength; i++) {
flag = true;
for (int j = 0; j < paraLength; j++) {
if (data[i + j] != paraString.data[j]) {
flag = false;
break;
}// Of if
}// Of for
if (flag) {
return i;
}// Of if
}// Of for
return -1;
}// Of locate
/**
* Get a substring
*
* @param paraStartPosition The start position.
* @param paraLength The length of the new substring
* @return The new MyString object.
*/
public MyString subString(int paraStartPosition, int paraLength) {
MyString resString = new MyString();
if (paraLength > length || paraLength <= 0 || paraStartPosition < 0 || paraStartPosition > this.length) {
System.out.println("Invalid input");
} else {
int count = 0;
for (int i = paraStartPosition; count < paraLength; i++) {
resString.data[count++] = this.data[i];
}// Of for
//resString.length = paraLength;
}// Of if
return resString;
}// Of subString
/**
* main, test
*
* @param args
*/
public static void main(String[] args) {
MyString a = new MyString("Good morning");
System.out.println(a);
MyString mor = new MyString("mor");
MyString my = new MyString("my");
MyString empty = new MyString("");
System.out.println(a.locate(mor));
System.out.println(a.locate(my));
System.out.println(a.locate(empty));
System.out.println(a.subString(0, 3));
System.out.println(a.subString(2, 4));
System.out.println(a.subString(20, 4));
}// main
}// Of class MyString
有参构造函数加了对参数字符串长度的判断,subString和locate方法也加上了对无效输入的判断,以免越界,增加一点健壮性。
20.小结
- 面向对象与面向过程相比, 有哪些优势? 注: 第 1 - 10 天的程序, 就是面向过程的.
复用性高,低耦合
- 比较线性表和链接的异同.
同:
1.都储存同一数据类型
2.线性结构
3.都可以顺序存取
异:
1.链接不用事先规定最大容量
2.链接增删元素时不用移动其他元素,线性表需要成块移动
3.链接不占用连续内存空间,线性表占用连续内存空间
4.链接增删效率高,线性表查找效率高,侧重不同
- 分析线性表和链接的优缺点.
线性表:
优点:支持随机存取,查找方便,O(1)
缺点:占用大量连续内存空间,增删元素时有大量移动O(n)
链表:
优点:增删元素效率高,O(1),不需要移动,占用的内存空间不一定相邻
缺点:查找效率慢,O(n)
- 分析调拭程序常见的问题及解决方案.
常见问题:极端情况的报错,如数组越界、空指针
解决:打断点debug、用打印语句判断是否运行了这一步等找出现错误的原因;分析处理边界极端情况的方法
- 分析链队列与循环队列的优缺点.
循环队列对空间的利用率高,节约空间,但是会出现满队列的情况,适用于已知数据规模的情况
链队列用分散的内存空间储存,不会有溢出的情况,占用空间更多
- 第 18 天建立的两个队列, 其区别仅在于基础数据不同, 一个是 int, 一个是 char. 按这种思路, 对于不同的基础数据类型, 都需要重写一个类, 这样合理吗? 你想怎么样?
不合理
可以用通配符,代码如下
通配符
package day20;
import java.util.ArrayList;
/**
* 解决换数据类型就重写类的方法
*
* @param <T> 数据类型
* @author pzf
*/
public class CircleAnyQueue<T> {
// attributes
final static int MAX_SPACE = 10;
int head, tail;
ArrayList<T> data;
/**
* contractor
*/
public CircleAnyQueue() {
data = new ArrayList<T>(MAX_SPACE);
head = 0;
tail = 0;
}// Of contractor
/**
* 进队
*
* @param value
* @return
*/
public boolean enqueue(T value) {
if ((tail + 1) % MAX_SPACE == head) {
System.out.println("Full");
return false;
}// Of if
data.add(tail % MAX_SPACE, value);
tail = (tail+1) % MAX_SPACE;
return true;
}// Of enqueue
/**
* 出队
*
* @return T 队头元素
*/
public T dequeue(){
if( tail == head ){
System.out.println("Empty");
return null;
}// Of if
T res = this.data.get(head % MAX_SPACE);
head = (head + 1) % MAX_SPACE;
return res;
}// Of dequeue
/**
* 重写toString
*
* @return 字符串
*/
public String toString(){
String res = "";
int count = head;
while (count % MAX_SPACE != tail) {
//res += data[count % MAX_SPACE] + ", ";
res += (T)data.get(count % MAX_SPACE) + ", ";
count++;
}// Of while
if(res.length()>0){
res = res.substring(0,res.length()-2);
}// Of if
return res;
}// Of toString
/**
* Test,main
*
* @param args
*/
public static void main(String[] args) {
CircleAnyQueue<Integer> a = new CircleAnyQueue<>();
CircleAnyQueue<String> b = new CircleAnyQueue<>();
for (int i = 0; i < 10; i++) {
a.enqueue(i);
b.enqueue("abc"+i);
}// Of for
System.out.println(a);
System.out.println(b);
}// Of main
}// Of class CircleAnyQueue
data如果用数组,似乎new的时候不支持通配符,会报错,没研究出来怎么解决,就换了ArrayList
总结:
用了3天完成1-20天的内容,一年没碰过Java,复健复健,后面的内容算是比较陌生,慢慢学习。
代码规范继续完善,不看闵老师的注释真写不出那种简洁的注释,或许写出来也只有我自己看得懂。