提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
Day11——顺序表(一)
顺序表是什么?
1.顺序表从属于线性表这一个大类 。线性表,全名为线性存储结构,使用线性表存储数据的方式可以这样理解,即“把所有数据用一根线儿串起来,再存储到物理空间中”。
2.物理空间中的不同存储方式派生出顺序表与链表两个分集,其中顺序表的特点在于逻辑上相连的空间在物理地址上也是绝对相连的,这种绝对相连的过程构成了线性表的顺序存储,从而简称“顺序表”。
3.具体来说顺序表,其用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表在逻辑结构上相邻的元素存储在连续的物理存储单元中。
即:通过数据元素物理存储的连续性来反应元素之间逻辑上的相邻关系。又因为地址空间的定长特点,可以实现随机存取。
一般来说,所有的编程语言模拟顺序表的方法都是通过数组,因为数组的存储与逻辑结构与顺序表几乎完全一致。
Java复习:重写与重载
1.重写(Override)
从字面上看,重写就是重新写一遍的意思。其实就是子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时) 都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
在类中,我们往往会对构造函数进行重载。
2.重载(Overloading)
在一个类中,同名的方法如果有不同的参数列表 (参数类型不同、参数个数不同甚至是参数顺序不同) 则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。
练习代码
package datastructure;
/**
* Sequential list.
*
* @author Wanxiang Luo 2858442831@qq.com
*/
public class SequentialList {
/**
* The maximal length of the list. It is a constant.
*/
public static final int MAX_LENGTH = 10;
/**
* The actual length not exceeding MAX_LENGTH. Attention: length is not only the
* member variable of Sequential list, but also the member variable of Array. In
* fact, a name can be the member variable of different classes.
*/
int length;
/**
* The data stored in an array.
*/
int[] data;
public SequentialList() {
length = 0;
data = new int[MAX_LENGTH];
}// Of the first constructor
/**
*********************
* Construct a sequential list using an array.
*
* @param paraArray The given array. Its length should not exceed MAX_LENGTH.
* For simplicity now we do not check it.
*********************
*/
public SequentialList(int[] paraArray) {
data = new int[MAX_LENGTH];
length = paraArray.length;
// Copy data.
for (int i = 0; i < paraArray.length; i++) {
data[i] = paraArray[i];
} // Of for i
}// Of the second constructor
/**
*********************
* Overrides the method claimed in Object, the superclass of any class.
*********************
*/
public String toString() {
String resultString = "";
if (length == 0) {
return "empty";
} // Of if
for (int i = 0; i < length - 1; i++) {
resultString += data[i] + ",";
} // Of for i
resultString += data[length - 1];
return resultString;
}// Of toString
/**
*********************
* Reset to emppty
*********************
*/
public void reset() {
length = 0;
}// Of reset
public static void main(String args[]) {
int[] tempArray = { 1, 4, 6, 7 };
SequentialList tempFirstList = new SequentialList(tempArray);
System.out.println("Initialized, the list is: " + tempFirstList.toString());
System.out.println("Again, the list is: " + tempFirstList);
tempFirstList.reset();
System.out.println("After reset, the list is: " + tempFirstList);
}// Of main
}// Of class SequentialList
1.对构造函数SequentialList()进行了重载,无参构造器用于初始化顺序表,有参构造器用于数据的快速复制
2.对Object类中的toString方法进行了重写,两次输出分别是直接调用对象本身的方法toString()与toString()本身重写特征下影响的println()方法中对于此对象的识别。
运行结果
小结
顺序表很容易被人脑接受,是因为人的思考模式本身是按照一定顺序,提取围绕目标或问题的相关信息。但是它却要进过一定的程序抽象才能被计算机接受。因为顺序表要对仅仅局限于一对一的存储结构中,实现对于全局数据的感知。很多逻辑结构,人为感知很简单,因为人脑可以快速提取一个顺序段的信息,但是,用程序抽象却格外困难。因为,本质上,这是逻辑丰富与物理存储单一的矛盾。
而机器学习正是对能通过经验自动改进的计算机算法的研究。
Day12——顺序表(二)
查找
1.含义:给出任意一个数,在顺序表中查找此元素首次出现时的下标号。
2.默认的顺序查找方法就是从头到尾遍历数据(什么方向都行),一但发现合适的数据就返回下标即可,如果结束遍历后都没有发现合适元素就返回一个定义内的下标非法的数值(比如-1)
3.若表为顺序表,可用二分法O(logn),来提高查找效率
/**
*********************
* Find the index of the given value. If it appears in multiple positions,
* simply return the first one.
*
* @param paraValue The given value.
* @return The position. -1 for not found.
*********************
*/
public int indexOf(int paraValue) {
int tempPosition = -1;
for (int i = 0; i < length; i++) {
if (data[i] == paraValue) {
tempPosition = i;
break;
} // Of if
} // Of for
return tempPosition;
}// Of indexOf
使用for循环将传入值与顺序表的值一一比对,匹配上则将数据位置赋值给tempPosition并返回,返回值为-1时表明未找到
插入
1.插入前需注意:顺序表是否满,插入位置是否合理
2.插入数据时:从尾到头遍历至该位置paraPosition,data[i] = data[i - 1];不断用 前一个元素覆盖当前元素。
3.然后只需要在图中的原paraValue位置修改为我们的插入元素即可实现前插入
public boolean insert(int paraPosition, int paraValue) {
if (length == MAX_LENGTH) {
System.out.println("List full.");
return false;
} // Of if
if ((paraPosition < 0) || (paraPosition > length)) {
System.out.println("The position " + paraPosition + " is out of bound.");
return false;
}
// From tail to head. The last one is moved to a new position.
// Because length < MAX_LENGTH, no exceeding occurs.
for (int i = length; i > paraPosition; i--) {
data[i] = data[i - 1];
} // Of for i
data[paraPosition] = paraValue;
length++;
return true;
}// Of insert
删除
1.从paraPosition位置开始遍历,遍历过程中:data[i] = data[i + 1];
,后一位的数据据直接覆盖前一位,(需使用从前向后遍历,否则无法实现向前覆盖)
2.操作完成,末尾元素出现了重复,因此需要我们逻辑上对其进行删除(令length-1)
/**
*********************
* Delete a value at a position.
*
* @param paraPosition The given position.
* @return Success or not.
*********************
*/
public boolean delete(int paraPosition) {
if ((paraPosition < 0) || (paraPosition >= length)) {
System.out.println("The position " + paraPosition + " is out of bounds.");
return false;
} // Of if
// From head to tail
for (int i = paraPosition; i < length - 1; i++) {
data[i] = data[i + 1];
} // Of for i
length--;
return true;
}// Of delete
代码主体
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String args[]) {
int[] tempArray = { 1, 4, 5, 9 };
SequentialList tempFirstList = new SequentialList(tempArray);
System.out.println("After initialization, the list is: " + tempFirstList.toString());
System.out.println("Again, the list is: " + tempFirstList);
int tempValue = 4;
int tempPosition = tempFirstList.indexOf(tempValue);
System.out.println("The position of " + tempValue + " is " + tempPosition);
tempValue = 5;
tempPosition = tempFirstList.indexOf(tempValue);
System.out.println("The position of " + tempValue + " is " + tempPosition);
tempPosition = 2;
tempValue = 5;
tempFirstList.insert(tempPosition, tempValue);
System.out.println("After inserting " + tempValue + " to position " + ", the list is: " + tempFirstList);
tempPosition = 8;
tempValue = 10;
tempFirstList.insert(tempPosition, tempValue);
System.out.println(
"After inserting " + tempValue + " to position " + tempPosition + ", the list is: " + tempFirstList);
for (int i = 0; i < 8; i++) {
tempFirstList.insert(i, i);
System.out.println("After inserting " + i + " to position " + i + ", the list is: " + tempFirstList);
} // Of for i
tempFirstList.reset();
System.out.println("After reset, the list is: " + tempFirstList);
}// Of main
运行结果
总结
1.顺序表的插入,删除,查找的时间复杂度都是O(n)
2.要注意把握逻辑结构与物理结构之间的度。我们谈论一个算法时,其逻辑结构更多是完成算法的设计,而实际的物理结构是完成算法的实现,因此在算法的实现过程中,我们必须时刻警惕计算机物理结构的特性,因为这种特性可能时时刻刻都会与我们的常规逻辑思维作对
Day13——链表
数据结构中的链表
特点:
1.逻辑上相邻的元素不一定在物理的位置上也相邻
2.实现插入和删除的效率更高,其插入只需要创建一个新结点并且修改链表中某逻辑上相连的两个结点之间的链接指针的关系即可,故时间复杂度为O(1)。
3.无法实现随机存取,这导致其无法像顺序表那样轻松用O(1)去访问特定位置,从而必须采用顺序存取对数据进行访问,访问的时间复杂度为O(1)。
Java中链表的实现
C语言提供的“指针”,更加类似于汇编语言的间接寻址能力,可以赋予程序员对内存操作的极大的灵活性,它使得程序员可以更直接地“操纵”硬件,获得高性能用 。C 语言比较容易获得地址信息,但用 Java 却做不到。Java中是如何实现链表的呢?
Java中用引用(Reference) 代替指针的作用,引用的声明类似与声明变量。声明一个孤单的对象时,会在内存中为其单独开辟一个小空间,这个空间内部是用于存放后期使用new为其分配空间后,用于存放分配空间的起始地址的。
下面的代码用于定义节点
/**
* 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 node. The data is never used.
*/
Node header;
/**
*********************
* Construct an empty linked list.
*********************
*/
public LinkedList() {
header = new Node(0);
}// Of the first constructor
链表的清空
让头节点的next指向null即可
对于Java来说,若系统发现不能通过引用访问的空间,则系统会自动将其回收。
/**
*********************
* Reset to empty. Free the space through garbage collection.
*********************
*/
public void reset() {
header.next = null;
}// Of reset
链表的遍历
打印出链表数据:重写toString方法,先判断该链表是否为空,为空返回empty。第一步是创建工作结点,因为我们遍历只要求访问有效数据区域,因此工作结点是头指针的下个位置Node tempNode = header.next
;第二步,通过while操作完成有效区域的迭代并输入到输出字符。最后返回输出字符。
/**
*********************
* 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
链表数据的查找
1.可与顺序表的查找作比较,相同点是都先设置默认下标值tempPosition为-1,若在空表情况下,while()循环不会执行,则直接跳过while直接返回时返回非法值告诫查无此数之作用。
2.不同点是在原来遍历基础上同步更新一个计数器,每进过一个节点,计算器自加一。我们的代码定义为头结点不纳入有效计数,那么第一个有效结点就是下标0(全链的第二个结点),即计算器初始值为0.
/**
*********************
* Locate the given value. If it appears in multiple positions, simply return
* the first one.
*
* @param paraValue The given value.
* @return The position. -1 for not found.
*********************
*/
public int locate(int paraValue) {
int tempPosition = -1;
Node tempNode = header.next;
int tempCurrentPosition = 0;
while (tempNode != null) {
if (tempNode.data == paraValue) {
tempPosition = tempCurrentPosition;
break;
} // Of if
tempNode = tempNode.next;
tempCurrentPosition++;
} // Of while
return tempPosition;
}// Of locate
链表的删除与插入
1. 删除操作
将前驱结点0的next指向我们的目标结点1的下一个结点
public boolean delete(int paraPosition) {
if (header.next == null) {
System.out.println("Cannot delete element from an empty list.");
return false;
} // Of if
Node tempNode = header;
for (int i = 0; i < paraPosition; i++) {
if (tempNode.next.next == null) {
System.out.println("The position " + paraPosition + " is illegal.");
return false;
} // Of if
tempNode = tempNode.next;
} // Of for i
tempNode.next = tempNode.next.next;
return true;
}// Of delete
2.插入操作
1.找到位置的复杂度为O(n),插入的复杂度为O(1)
2.例如:在结点2和结点1之间插入结点5,需先找到结点2的位置,后将新结点5的next指向我们的目标结点(结点2),前结点1的next指针指向新结点5。
注:若操作顺序颠倒,会导致结点5无法找到后续结点
public boolean insert(int paraPosition, int paraValue) {
Node tempNode = header;
Node tempNewNode;
for (int i = 0; i < paraPosition; i++) {
if (tempNode.next == null) {
System.out.println("The position " + paraPosition + " is illegal.");
return false;
} // Of if
tempNode = tempNode.next;
} // Of for i
// Construct a new node.
tempNewNode = new Node(paraValue);
// Now link them.
tempNewNode.next = tempNode.next;
tempNode.next = tempNewNode;
return true;
}// Of insert
模拟
public static void main(String args[]) {
LinkedList tempFirstList = new LinkedList();
System.out.println("Initialized, the list is: " + tempFirstList.toString());
for (int i = 0; i < 5; i++) {
tempFirstList.insert(0, i);
} // Of for i
System.out.println("Inserted, the list is: " + tempFirstList.toString());
tempFirstList.insert(5, 100);
// tempFirstList.delete(4);
// tempFirstList.delete(2);
System.out.println("Deleted, the list is: " + tempFirstList.toString());
tempFirstList.delete(0);
System.out.println("Deleted, the list is: " + tempFirstList.toString());
for (int i = 0; i < 5; i++) {
tempFirstList.delete(0);
System.out.println("Looped delete, the list is: " + tempFirstList.toString());
} // Of for i
}// Of main
运行结果
Day14——栈
深入理解栈的特性
前两天的任务中分别复习,且加深印象了数据结构中的顺序表和链表。今天将开启对栈的深入学习
计算机中的“ 栈 ”(Stack)可以理解为暂时存储的容器
。比如在函数调用时,底层编译器会把我们当前的数据放于这个临时的容器中存储,避免在进入函数后当前上下文环境的信息丢失,之后,待到函数返回后再从Stack中取出。
栈和队列是两种重要的线性结构。从数据结构角度看,栈和队列也是线性表,其特殊性在于栈和队列是线性表操作的子集,它们是操作受限的线性表
。而栈按照后进先出
(Last In First Out,简称LIFO)或先进后出
(First In Last Out,简称FILO)的原则进行线性表,因此,栈又称为LIFO表或FILO表。
栈的操作
栈的创建
由于栈可看成“ 运算受限的线性表 ”,故按照创建线性表相似的方法创建栈
由构造函数完成栈深度(长度)初始化,空间开辟遵循静态链表开辟的方法——使用new开辟固定“MAX_DEPTH”大小的空间。
因为栈是有栈顶处进行插入和删除操作,故定义数组的最右侧为栈顶
/**
* The depth.
*/
public static final int MAX_DEPTH = 10;
/**
* The actual
*/
int depth;
/**
* The data
*/
char[] data;
/**
*********************
* Construct an empty char stack.
*********************
*/
public CharStack() {
depth = 0;
data = new char[MAX_DEPTH];
}// Of the first constructor
/**
*********************
* Overrides 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
补充两种表示形式:
1.depth默认指向尾元素后面的空位
data[depth] = paraChar;
depth++;
2.栈顶指针指向末尾元素本身
top++
data[top] = paraChar;
出栈操作
出栈操作可以看做入栈的逆过程
public char pop() {
if (depth == 0) {
System.out.println("Nothing to pop.");
return '\0';
} // Of if
char resultChar = data[depth - 1];
depth--;
//栈顶指针指向栈顶有效元素的上方的一个无数据位
return resultChar;
}// Of pop
代码模拟
public static void main(String args[]) {
CharStack tempStack = new CharStack();
for (char ch = 'a'; ch < 'm'; ch++) {
tempStack.push(ch);
System.out.println("The current stack is: " + tempStack);
} // Of for ch
char tempChar;
for (int i = 0; i < 12; i++) {
tempChar = tempStack.pop();
System.out.println("Poped: " + tempChar);
System.out.println("The current stack is: " + tempStack);
} // Of for i
}// Of main
运行结果
Day15——栈的应用:括号匹配
任务描述
1.检查一个字符串的括号是否匹配. 所谓匹配, 是指每个左括号有相应的一个右括号与之对应, 且左括号不可以出现在右括号右边. 可以修改测试字符串, 检查不同情况下的运行.
2.例如[]{}
是正常匹配。(]
不算,]][[
不算,(({{[]}}))
是匹配,(([[]}))
不算。
当然我们的符号中间可以加任何其余干扰数字,(1 + 3 / ( 90 % {1 + {99 + [3 - 1] + 3} / 9} - 9) * 101)
是匹配。
代码实现
package good_better;
import org.omg.CORBA.PUBLIC_MEMBER;
/**
* Is the bracket matching?
*
* @author Wanxiang Luo 2858442831@qq.com
* @return Match or not.
*/
public class E_zhan {
public static final int MAX_DEPTH = 10 ;
int depth;
char[] data;
public E_zhan() {
depth = 0;
data = new char [MAX_DEPTH];
}
public String toString() {
String resultString = " ";
for (int i = 0; i < data.length; i++) {
resultString += data[i];
}
return resultString;
}
public boolean push (char paraChar) {
if (depth == MAX_DEPTH) {
System.out.println("Stack full.");
return false;
}//of if
data[depth] = paraChar;
depth++;
return true;
}
public char pop() {
if (depth == 0) {
System.out.println("Nothing to pop.");
return '\0';
}//of if
char resultChar = data[depth-1];
depth--;
return resultChar;
}
public static boolean bracketMatching(String paraString) {
// Step 1. Initialize the stack through pushing a '#' at the bottom.
E_zhan tempStack = new E_zhan();
tempStack.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 '{':
tempStack.push(tempChar);
break;
case ')':
tempPopedChar = tempStack.pop();
if (tempPopedChar != '(') {
return false;
} // Of if
break;
case ']':
tempPopedChar = tempStack.pop();
if (tempPopedChar != '[') {
return false;
} // Of if
break;
case '}':
tempPopedChar = tempStack.pop();
if (tempPopedChar != '{') {
return false;
} // Of if
break;
default:
// Do nothing.
}// Of switch
} // Of for
tempPopedChar = tempStack.pop();
if (tempPopedChar != '#') {
return false;
} // Of if
return true;
}// Of bracketMatching
public static void main(String[] args) {
// TODO Auto-generated method stub
// E_zhan tempStack = new E_zhan();
/*for(char ch = 'a'; ch < 'm'; ch++) {
tempStack.push(ch);
System.out.println("The current stack is :" + tempStack);
}//of for ch
char tempChar ;
for (int i = 0; i < 12; i++) {
tempChar = tempStack.pop();
System.out.println("Poped: " + tempChar);
System.out.println("The current stack is : "+ tempStack );
}//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 for main
}
运行结果
小结
1.当第1个括号到达时,它期待第4个括号与它匹配,但第2个括号会先到达,因此第1个括号会暂时等待。第2个括号期待第3个括号,第3个括号到达后,与第2个括号匹配,消掉一对括号,如此循环匹配下去。
2.我们使用栈暂存待匹配的括号。遇到左括号时,将其先压入栈;遇到右括号,从栈弹出一个元素,比较两是否匹配。
3.本代码使用前可以预先在栈底填入一个非法字符,可以方便避免一些空栈的麻烦判断,是一种非常常用的栈技巧
Day16——递归
1. 什么是递归?
答:递归,就是在运行的过程中调用自己。
2.递归的必要条件
其一:子问题须与原始问题为同样的事,且更为简单;
其二:不能无限制地调用本身,须有个出口,化简为非递归状况处理。
递归模板:
我们知道递归必须具备两个条件,一个是调用自己
,一个是有终止条件
。这两个条件必须同时具备,且一个都不能少。并且终止条件必须是在递归最开始的地方,也就是下面这样
public void recursion(参数0) {
if (终止条件) {
return;
}
recursion(参数1);
}
下面的代码是错误案例,不能把终止条件写在递归结束的位置
如果这样的话,递归永远退不出来了,就会出现堆栈溢出异常(StackOverflowError)
public void recursion(参数0) {
recursion(参数1);
if (终止条件) {
return;
}
}
但实际上递归可能调用自己不止一次,并且很多递归在调用之前或调用之后都会有一些逻辑上的处理,比如下面这样
public void recursion(参数0) {
if (终止条件) {
return;
}
可能有一些逻辑运算
recursion(参数1)
可能有一些逻辑运算
recursion(参数2)
……
recursion(参数n)
可能有一些逻辑运算
}
3.实例分析
0到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) {
// Basis
return 0;
} // Of if
return sumToN(paraN - 1) + paraN;
}// Of sumToN
斐波那契数列
我们知道斐波那契数列当前的值是前两个值的和,也就是fibonacci(n) =fibonacci(n - 1) + fibonacci(n - 2)
所以引出代码
/**
*********************
* Fibonacci sequence.
*
* @param paraN The given value.
* @return
*********************
*/
public static int fibonacci(int paraN) {
if (paraN <= 0) {
// Negative value are invalid. Index 0 corresponds to the first element
return 0;
}
if (paraN == 1) {
// Basis.
return 1;
} // Of if
return fibonacci(paraN - 1) + fibonacci(paraN - 1);
}// Of fibonacci
数据模拟
/**
*********************
* 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 i
}// Of main
运行结果
Day17——链表实现队列
队列的底层逻辑
定义:
队列是一种运算受限的线性表
,特殊之处在于它只允许在表的前端(front)进行删除操作
,而在表的后端(rear)进行插入操作
,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。当队列中没有元素时称为空队列
1.队列的基本操作近似于生活中无处不在的排队,对于每一个排队的人来说,来插队的家伙都不是那么人喜欢。
2.队列的基本操作中不允许有中途插队的选手,而对于线性表来说是可以在队中插入并麻烦之后的所有人都后退一步,故队列是运算受限的线性表。
3.队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队(enqueue),从队列中删除一个队列元素称为出队(Dequeue)。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
用链表实现队列
1.定义结点
class Node {
int data;
Node next;
/**
*******************
* The constructor.
*
* @param paraValue The data.
*******************
*/
public Node(int paraValue) {
data = paraValue;
next = null;
}// Of the constructor
}// Of class Node
2.链队列的基本属性
Node header;
Node tail;
/**
*********************
* Construct an empty sequential list.
*********************
*/
public LinkedQueue() {
header = new Node(-1);
tail = header;
}// Of the first constructor
@override
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
3.入队
/**
*********************
* 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
4.出队
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 dequeu
}// Of enqueue
代码模拟
/**
*********************
* 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
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
运行结果
Day18——循环队列
寻常队列队头删除,队尾插入。对头元素出队后可能出现队头空间被浪费
的情况。如果不断对队列进行入队、出队、入队、出队…首尾指针位置最终会变得非常大
,但是队列内的数据却还是非常少,而且之前出队后空余的位置无法被重复使用,照成极大浪费。
特点
1、循环队列是队列的顺序存储结构
2、循环队列用判断是否为空利用 Q.front=Q.rear
3、循环队列头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置
4、循环队列通过浪费一个空间,利用(Q.rear+1)%maxSize=Q.front判断队列是否为满,以此解决队列空间浪费问题(如下图所示)
代码实现
1.类的属性,构造器与遍历方法
/**
* The total space. One space can never can never be used
*/
public static final int TOTAL_SPACE = 10;
int[] data;
/**
* The index for calculating the head. The actual head is head % TOTAL_SPACE.
*/
int head;
/**
* The index for caluculating the tail.
*/
int tail;
/**
*******************
* The constructor
*******************
*/
public CircleIntQueue() {
data = new int[TOTAL_SPACE];
head = 0;
tail = 0;
}// Of the first constructor
/**
*********************
* 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
入队
/**
*********************
*
* @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
出队
/**
*********************
*
* @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
main方法部分
/**
*********************
* 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
运行结果
小结
1.虽然循环队列的使用不像普通链表那样方便,却极适用与空间有限的硬件条件。
2.循环队列就体现其对于环对空间最大化利用的特性,弥补了单一顺序表使用队列时前端空间的浪费
Day19——自定义字符串
1 String 是 Java 常用的类, 这里重新实现下部分功能.
2 转义符 , 有了它才能正常打印引号.
3 简单的越界检查.
定义构造器与遍历方法
鉴于字符串的单个字符本身信息很少,用链表实现字符串冗余度大。故使用顺序表实现字符串。
/**
* The maximal length.
*/
public static final int MAX_LENGTH = 10;
/**
* The actual length
*/
int length;
/**
* The data.
*/
char[] data;
/**
*********************
* Construct an empty char array.
*********************
*/
public MyString() {
length = 0;
data = new char[MAX_LENGTH];
}// Of the first 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 toString
/**
查找子串的方法
1.规定主串长度为(1 ≤ mainLength ≤ N)
,字串长度为(1 ≤ patternLength ≤ mainLength)
,且模式串可以吻合到主串的任何一个不越界的连续部分
2.模式串逐个与主串切合去试着查看是否可以全部匹配。移动贴合的次数是mainLength - patternLength + 1
。
3.每次贴合时都要对模式串进行循环,进行patternLength
次逐个字符的从串遍历。
4.总执行次数是(mainLength - patternLength + 1)× patternLength
,主串长度为N,模式串长度为M,那么这个算法的时间复杂度就是O(MN)
/**
*********************
* Locate the position of a substring.
*
* @param paraMyString
* The given substring.
* @return The first 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 of
} // Of for i
return -1;
}// Of locate
外层循环负责模式串移动位置,mainLength - patternLength + 1
显示剩余移动次数。内层循环负责检测当前主串的部分与模式串全体是否连续相等
,一旦一次不等,那就取false返回,但凡又一次全等,tempMatch会保持不变,从而可以返回匹配,i刚好是本轮位置的第一个字符位。
字符串分片
给定第一个字符下标,然后给出片长
/**
*********************
* 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
main方法部分
/**
*********************
* 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
运行结果
分析:
1.寻找模式串“ik" 在"I like ik”中的位置,返回的结果是3,字符串中的空格也算一个位置
2.寻找模式串“ki" 在"I like ik”中的位置,因为不存在,故返回-1
3.substring方法传入两个实参分别代表从原数组何位置开始和录入开始位置往后多少位。
传入(1,2),则从"I like ik.”
的第一位开始,即第一个I后的空格位。往后录入两为,故返回" l"
3.最后一个案例查询超过原字符串的长度,故报错
小结
1.字符串在计算机领域有着非常大的作用,个人认为可以把字符串看成,计算机世界和现实世界交流和传递信息的桥梁。字符串将不同模样,千奇百怪的数据统一起来(预处理),然后交付给计算机进行高速度机械式地计算,最后输出到我们现实中来。可以认为,字符串就是现实世界到我们计算机世界的第一层抽象。
2.11到19节的主要内容到此为止,线性表在计算机领域的应用远远不止与此。个人认为只有在不断的代码练习中,不断地接触不熟悉的领域,才能在机器学习算法的路上走远。
Day20——小结
1.面向对象与面向过程相比, 有哪些优势?
答:个人认为有以下几点:
1.面向过程编程难以在大型的复杂的程序开发中发挥作用,实际应用开发中往往是互相嵌套互相调用的程序占据主导,程序之间的关系更加复杂联系更紧密,而非以单一一条主线的形式出现。
2.面向对象编程则把复杂问题简单化,将现实世界不同的群体的共同特性和行为抽象出来,模拟成一个个逻辑设计的类。例如将’呼吸’、‘能直立行走’、'能用语言交流’等共性从人这个事物中抽象出来,形成一个类,称作人‘类’
,那么‘呼吸’是其方法(行为),‘能直立行走’和‘能用语言交流’是其属性。再者不同的人种或者肤色可以独立出来,成为人类的子类。继续归类下去,能形成更多的子类,也就有更多的属性被囊括。最后不难推断出,在存储允许的条件下,人和其相关的事物都会归类其中。同样,理论上现实世界所有可以用语言描述的物种全可封装于某个类中。‘面向对象编程’记良方专治解决多元的,复杂多变的,庞大的问题。
3.面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态)。
4.面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能。
5.面向对象的代码更符合人的思维。代码,更加易扩展、易复用、易维护。
2.比较顺序表和链表的异同.
不同点:
1.线性表的储存采用连续的存储空间,链表采用链式的存储空间
2.顺序表支持随机访问,链表不支持随机访问
3.顺序表的插入和删除时间复杂度为O(n),链表的时间复杂度为O(1)
相同点:
1.查找的时间复杂度都为O(n)
3.分析顺序表和链表的优缺点.
顺序表的优点:
1.数据在物理上连续存储,方便使用
2.可随机访问
3.存储长期不改动数据占优势
缺点:
1.不适用数据经常改动的场景
2.易造成小空间的浪费
3.插入和删除操作效率低
链表的优点:
1.存储在逻辑上连续,节省空间
2.插入和删除方便
3.适用于数据经常改动的场景
缺点:
1.不可随机访问
2.频繁访问时效率低
4.分析调试程序常见的问题及解决方案
1.编译错误:通过返回的错误信息就能找出错误。并且记住错误,下次注意。
2.运行错误:检查代码的循环和递归处设置是否合理,是否有合理加入break。
3.输出错误:检查代码的逻辑是否符合题意,重新分析需要解决的问题,找到逻辑不通畅的位置,用自己的语言再捋一遍,最后反映到代码上
5.分析链队列与循环队列的优缺点.
链队列:
优点:链表存储,不用判断表是否满,操作方便
缺点:容易出现首尾指针位置最终非常大`,但是队列内的数据却还是非常少的情况,造成空间冗余
循环队列:
优点:空间利用高,不会造成冗余,
缺点:顺序表存储,整体大小是有限的,需要判断是否满
6.第 18 天建立的两个队列, 其区别仅在于基础数据不同, 一个是 int, 一个是 char. 按这种思路, 对于不同的基础数据类型, 都需要重写一个类, 这样合理吗? 你想怎么样?
不合理,这样处理不适合在两种数据类型差别不大的情况下,不满足面向对象思想,且会显得代码冗杂。可以使用泛型处理。