数据结构基本介绍(上)
*本文为《数据结构(Java语言描述)》一书的线上笔记
二.Java类与信息隐藏
- 面向对象程序设计
一种程序设计方法,将数据存储在对象中,通过方法来调用操作对象。类是创建对象和方法的机制。
三.集合类###
数组
注意:
*数组变量是引用变量,使用new操作符来创建。int[] scores = new int[4],scores将引用具有4个整型元素的数组。
*在使用数组前一定要先创建数组。要防止数组越界。
*使用赋值语句"="时,赋的是数组的引用。
*若要克隆出一个独立数组,则使用方法clone();
使用数组实现集合类
四.链表
链表需要动态地缩减和增长使用的内存。链表是一个接一个排列的元素序列,常用的程序设计方式是将每个元素与指向下一个元素的“指针”放置在一起,构成一个结点。其中,最后一个结点不再指向下一个结点。
代码示例(IntNote类):
声明:
//存储在该结点中的元素
private int date;
//最后一个结点为null
//在链表中引用下一个结点
private IntNode link;
public IntNode(int initalDate,IntNode initialLink){
date = initalDate;
link = initialLink;
}
//省略data,link的get,set方法。
操作链表:
在链表头添加新结点
首先,需要一个引向链表头结点的引用head。使用head = new IntNode(5,head),该语句首先创建一个数据为5的新结点,因为head存储的是下一个结点的引用,故该结点引向下一结点的引用。在链表头部删除结点
首先,需要一个引向链表头结点的引用head,删除第一个结点,只需使用head = head.getLink()。使head引向下一个结点。添加链表中非头部结点
首先,需要一个引向新结点预期位置之前的结点的引用(所选结点引用selection),
public void addNodeAfter(int element){
//调用该方法后,生成一个数据域为element,link域与selection.link相同的结点
//构造函数返回了引向新建结点的引用
link = new IntNode(element,link);
}
分析:selection.addNodeAfter(42),假设前一个结点的数据为20
1.selection为数据为20的结点的引用
2.link = new IntNode(42,link),其中,link来自所选结点,即link就是selection.link。该语句的右侧执行了构造函数,生成一个数据为42,link域与selecion.link相同的新结点。构造函数返回引向新创建结点的引用(理解成:修改所选结点的link域,使其引向新创建的结点)。删除链表中非头部结点
首先,需要一个引向新结点预期位置之前的结点的引用(所选结点引用selection)。要将刚才添加的结点删除,selection则应为引向数据为20的结点,我们只需将selection的link域赋值为link.link,即引向下下个结点的引用。
public void removeNodeAfter(){
link = link.link;
//潜在空指针异常
}
操作整个链表
使用静态(保证链表为空时依然可用)方法listLength获得链表的长度
public static int listLength(IntNode head){
//用于遍历链表的引用变量
IntNode cursor;
int answer = 0;
//当cursor引向尾结点时,cursor.link = null,条件不成立,跳出循环。
for(cursor = head;cursor != null;cursor = cursor.link){
answer ++ ;
}
return answer;
}
在for循环中,我们可以执行诸如查找元素这样的代码
- 复制一个链表
}
//普通方法不能复制空链表,空链表是以空引用的方式表示的
public static IntNode listCopy(IntNode source){
IntNode copyHead;
IntNode copyTail;
if(source == null){
return null;
}
//为新链表生成第一个结点
//头、尾结点都引向该结点
copyHead = new IntNode(source.date,null);
copyTail = copyHead;
//添加其余结点
while(source.link != null){
source = source.link;
copyTail.addNodeAfter(source.date);
copyTail = copyTail.link;//
}
return copyHead;
}
实际中,常常需要返回一个包含头尾结点引用的数组。此时只需新建一个数据长度为2的数组。将头尾结点的引用赋值给数组后返回数组即可。
五.通用程序设计
使用一些普遍适用于许多场合的类。
Java中的Object类型
Object的变量能保存引向任意类型的对象的引用。
*拓展转换
String s = new String("I am a String");
Object obj;
//拓宽转换
obj = s;
*削窄转换
s = (String)obj,需要强制类型转换
Object方法和通用方法####
Object方法:
*可能会出现隐式的类型错误。
通用方法(使用泛型):
六.栈
一种由有序数据项构成的数据结构,数据项的插入及删除只能在栈顶进行。是一种后进先出的数据结构(LIFO)。可以把栈类比成一个存储硬币的圆柱容器,该容器只有顶端一个开口。往其中放入硬币称为压栈(push),从最上面取出一个称为出栈(pop),只查看第一个硬币而不拿动称为peek。
一个简单的例子,实现带全括号的基本算术运算:
代码暂略
栈的数组实现:
- 变量声明与构造函数
//数据总个数
private int manyItems;
//栈底元素为data[0],栈顶元素为data[manyItems-1]
private E[] data;
public ArrayStack(){
final int INITIAL_CAPCITY = 10;
manyItems = 0;
data =(E[]) new Object[INITIAL_CAPCITY];
}
public ArrayStack(int initialCapcity){
if(initialCapcity < 0){
throw new IllegalArgumentException();
}
manyItems = 0;
data =(E[]) new Object[initialCapcity];
}
- 栈的基本操作
//改变栈的当前容量
public void ensureCapacity(int minimumCapacity){
E biggerArray[];
if(data.length < minimumCapacity){
biggerArray = (E[])new Object[minimumCapacity];
System.arraycopy(data,0,biggerArray,0,manyItems);
data = biggerArray;
}
}
//获得栈顶元素
public E peek(){
if(manyItems == 0){
throw new EmptyStackException();
}
return data[manyItems - 1];
}
//出栈
public E pop(){
E answer;
if(manyItems == 0){
throw new EmptyStackException();
}
answer = data[--manyItems];
data[manyItems] = null;
return answer;
}
//压栈
public void push(E item){
if(manyItems == data.length){
ensureCapacity(manyItems*2 + 1);
}
data[manyItems] = item;
manyItems ++;
}
- 复制一个副本
该类要实现Clone
public ArrayStackclone(){
ArrayStack answer;
try{
answer = (ArrayStack) super.clone();
}catch(CloneNotSupportedException e){
throw new RuntimeException("This class does not implement Cloneable");
}
answer.data = data.clone();
return answer;
}
- 4
栈的链表实现:
使用链表,不需要担心容量(链表可动态增减数据项)。
实例变量top是引向链表数据项头结点的引用。
七.队列
队列是一种先进先出(FIFO)的数据结构(类比排队)。只能在队尾插入数据,从队头取出数据。队列中的数据项和栈一样,可以是任意类型的。
与栈相结合,实现一个简单的工具:检测回文
public static boolean isPalindrome(String input){
/**
Character类是对单个字符进行操作。
*/
java.util.Queue q = new LinkedList<>();
java.util.Stack s = new Stack<>();
Character letter ;
//不匹配的点的数量
int misMatches = 0;
int i ;
for( i = 0; i< input.length();i++){
letter = input.charAt(i);
if(Character.isLetter(letter)){
q.add(letter);
s.push(letter);
}
}
while(! q.isEmpty()){
if(q.remove( ) != s.pop()){
misMatches++;
}
}
return (misMatches == 0);
}
队列的数组实现:
-
实现思路:
使用部分填充的数组实现队列。队列可以在两端访问数据,同样,已填充部分数组也需要在两端访问数据。使用变量front,rear分别表示已使用数组的第一项及最后一项。添加数组项时,只要不断的将rear增加1。这样一来,rear只增不减。到达数组末端后,数组中可能还有未填充的数组单元。
上图中,删除数据项A,B后,添加两个数据E,F到队尾的尾部。如果我们想再添加一个数据G,发现数组末端已经没有空余,我们把G添加到右图数组的data[0]。此时rear的下标为0。rear=0 < front = 2;此时,我们把数组看成环,这样的数组称为循环数组。
public class ArrayQueue {
private E[] data ;
private int manyItems ;//记录队列中数据项的个数
/**
* front data[front],连续向前,到达数组的末端,返回data[0]
*/
private int front;
private int rear;
public ArrayQueue(){
//默认数组大小
final int INITIAL_CAPACITY = 10;
manyItems = 0;
data = (E[])new Object[INITIAL_CAPACITY];
}
- 3
- 4
- 5
八.递归思想
递归:在方法的内部调用该方法;
递归思想:子任务与开始时要解决的问题相同,只不过是一具比较简单的版本。我们要做的,就是找出问题的简单版本。
简单的例子:
实现输入整数,输出它所有位。
public static void writerVertical(int number){
if(number < 0){
writerVertical(-1*number);
}else if(number < 10 ){
System.out.println(number);
}else{
writerVertical(number/10);
System.out.println(number % 10);
}
}
说明
- 1
- 2
- 3
- 4
- 5