[color=red]数据结构学习笔记之一
注:参考书籍为数据结构-严蔚敏编著 2011/11/28 下午[/color]
第一章:数据结构概述
一、什么是数据结构
1、作者开篇谈到:
一般来说解决一个具体的问题时,大致需要经过下列几个步骤:首先要从具体的问题抽象出一个适当的数学模型,然后设计一个解此数学模型的算法,最后编写出程序代码,进行测试、调整直至得到最终的解决方案。
总结为:现实中具体的问题—>数学模型—>算法程序—>解决方案
动作为:抽象提取、设计编码、测试调整
2、数学角度阐述:
寻求数学模型的实质是分析问题,从中提取操作的对象,并找出这些操作对象之间含有的关系,然后用数学的语言加以描述。
3、定义数据结构:
描述这类非数值计算问题的数学模型不再是数学方程,而是诸如表、树和图之类的数据结构,因此,简单来说,数据结构是一门研究非数值计算的程序设计问题中计算机的操作对象以及它们之间关系和操作等的学科,用一句话来说就是,数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
研究对象:1、集合2、线性结构3、树形结构4、图状结构(网状结构)
结构分类:1、数据的逻辑结构2、数据的物理结构(存储结构)
关系表示:1、顺序映像2、非顺序映像,两者分别对应为顺序存储结构、链式存储结构
二、算法和算法分析
1、算法的五个特性:有穷性、确定性、可行性、输入和输出
2、算法设计的要求:正确性、可读性、健壮性以及效率与低存储量需求
3、算法的度量:时间复杂度和空间复杂度
总结:编写代码设计算法时候首先先考虑算法的正确性,确保程序能够满足要求,在正确性的前提下再进一步考虑算法的可读性、健壮性、拓展性以及算法的效率等。
第二章:线性表
一、线性表的定义
线性结构的特点是:在数据元素的非空有限集中(1)存在唯一的一个被称做“第一个”的数据元素;(2)存在唯一的一个被称做“最后一个”的数据元素;(3)除第一个之外,集合中每个数据元素均只有一个前驱;(4)除最后一个元素之外,集合中每个数据元素均只有一个后继。
线性表是最常用并且最简单的一种数据结构,简单来说,一个线性表是n个数据元素的有限序列。至于每个数据元素的具体含义,在不同的情况下各不相同,既可以是一个数也可以是一个符号等等。
二、线性表的操作
线性表是一个相当灵活的数据结构,它的长度可根据需要增长或者缩短,即对线性表的数据元素不但可以进行访问,还可以进行插入和删除等操作。线性表存储方式有两种,顺序存储和链式存储,下面通过代码进行简单模拟操作。
三、代码模拟简单实现
线性表求交、求并、线性表查找、插入、删除元素等操作。
1、顺序存储方式:
参加具体代码模拟实现、功能性上模拟、算法效率另当别论了。
2、链表相关问题:
单循环链表、判断链表是否有环、判断两个链表是否相交、删除某个特定的链表结点等操作。
[color=red]线性表模拟接口-实现代码如下:[/color]
import java.util.List;
/**
* @author Administrator
*
* @description 线性表模拟测试接口
* @history
*/
public interface ListTest {
/**
*@description 求两个线性表集合的交集
*@return 返回两个集合的交集
*/
List jiaoJi(List aList,List bList);
/**
*@description 求两个线性表集合的并集
*@return 返回合并后的集合
*/
List bingJi(List aList,List bList);
/**
*@description 在某个index后插入一个元素
*@return 插入成功返回true反之返回false
*/
boolean insert(List list,int index,int value);
/**
*
*@description 查找某个元素是否存在
*@return 查找成功返回true反之返回false
*/
boolean findByValue(List list,Integer value);
/**
*@description 根据索引index删除某个位置上的元素
*@return 删除成功返回true反之返回false
*/
boolean deleteByIndex(List list,int index);
// boolean isCircle(List list);
// 判断单链表是否有环方法主要有两种
// 方法一、哈希,方法二、快慢指针
// boolean isXiangjiao(List aList,List bList);
// 判断两个单链表是否相交方法比较多
// 暴力处理、哈希、指针遍历(将其中一个尾指向头,从另外一个头指针开始遍历看是否能到达另外一个头部)等
// 如果要定位出相交点的位置,哈希处理方式在时间上有优势但是空间上是劣势-参考自编程之美
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Administrator
*
* @description 操作实现类,简单功能模拟实现而已
* @history
*/
public class ListTestImpl implements ListTest {
public List jiaoJi(List aList, List bList) {
// 采用最简单的方式实现,暂不考虑效率问题
List cList = new ArrayList();
int aLength = aList.size();
int bLength = bList.size();
Integer temp = null; //临时比较的值
for (int i = 0; i < aLength; i++) {
temp = aList.get(i);
int j = 0;
for (; j < bLength; j++) {
if (temp.equals(bList.get(j)))
break; //循环遍历到相同元素后跳出当前循环
}
//将满足条件的元素放置在新的list中
if (j < bLength) cList.add(temp);
}
return cList;
}
public List bingJi(List aList, List bList) {
// 因为是线性表的并集,元素可以重复的,这个和数学上的并集不同
// 暂时不考虑效率性能,采用方法为先对list排序好然后再求并集
List cList = new ArrayList();
List list1 = aList;
List list2 = bList;
// 对两个集合进行排序操作
Collections.sort(list1);
Collections.sort(list2);
int aLength = list1.size();
int bLength = list2.size();
int i=0,j=0; //下标标记值
while (i < aLength && j < bLength) {
if (list1.get(i) <= list2.get(j)) {
cList.add(list1.get(i));
i++;
} else{
cList.add(list2.get(j));
j++;
}
}
while(i< aLength){
cList.add(list1.get(i));
i++;
}
while(j cList.add(list2.get(j));
j++;
}
return cList;
}
public boolean insert(List list, int index, int value) {
// 直接采用ArrayList实现代码模拟实现了
// 顺序存储方式需要在某个index后添加元素需要遍历、移动、插入元素等操作
int length = list.size();
if (index >= length)
return false; //下标越界
// 采用转换方法或者直接采用自动装箱
list.add(index,Integer.valueOf(value));
return true;
}
public boolean findByValue(List list, Integer value) {
// 遍历一遍集合即可查询是否存在某个元素
int length = list.size();
int i = 0;
for (; i < length; i++) {
if (list.get(i).equals(value)) {
break;
}
}
if(i == length) return false;
else return true;
// 如果是排序好的则不需要直接遍历集合一遍,采用二分查找从而提高效率
// 二分法代码如下
/*int low = 0;
int high = list.size();
int middle = (low+high)/2;
while (low <= high) {
if (list.get(middle).equals(value) || list.get(low).equals(value)
|| list.get(high).equals(value)) {
return true; // 存在该元素,返回true
} else if (list.get(middle) > value) {
low++;
high = middle - 1;
middle = (low + high) / 2;
} else {
high--;
low = middle + 1;
middle = (low + high) / 2;
}
}
return false;*/
// 如果再次改变需求,集合中元素排序好并且有重复的数字,如何最好找出某个元素第一次出现的index
// 对上面二分法进行改造处理即可
}
public boolean deleteByIndex(List list, int index) {
// 直接采用底层实现处理,如果要具体模拟需要遍历、删除并且移动元素等操作
int length = list.size();
if(index >= length) return false;
else{
list.remove(index);
return true;
}
// 如果是链式存储方式删除某个结点改变下指向即可
// 如果删除某个没有源指向的结点,采用偷换结点的思路删除节点即可
}
}
第三章:栈和队列
栈和队列是两种重要的线性结构,从数据结构的角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限制的线性表,因此可以称为限定性的数据结构。
一、栈的定义
栈是限定在表尾进行插入或删除操作的线性表,栈的特定是先进后出。栈的存储方式有两种,一种是顺序栈另外一种是链式栈,下面只通过代码简单模拟栈的操作。
二、栈的应用
栈的应用主要有数制转换、括号匹配的检验、迷宫问题求解以及表达式求值。另外栈递归实现的经典例子有八皇后问题、汉诺塔问题等。
三、队列的定义
队列和栈有点不同,队列是一种先进先出得线性表,它只能够在表的一端进行插入另外一头进行删除操作。队列在程序设计中比较常见的例子是操作系统中的作业排队。双端队列、循环队列有时间再进一步演进,暂时先了解些基本概念。
[color=red] 学习过程中代码如下:[/color]
/**
* @author Administrator
*
* @description 栈的简单模拟测试类
* @history
*/
public class StackTest {
/**
*@description
*@param args
*/
public static void main(String[] args) {
// 栈的底层实现是数组形式继承自Vector
// public class Stack extends Vector {}
Stack stack = new Stack();
// 入栈操作
stack.push("world");
stack.push("hello");
// 出栈操作
while(!stack.empty()){
// 栈的特点先进后出,输出hello world
System.out.print(stack.pop()+" ");
}
// 初步学习Stack类的源代码
// Stack类的pop出栈方法
/*public synchronized E pop(){ //同步方法处理
E obj; //采用了泛型
int len = size(); //数组长度
obj = peek(); //获取栈顶元素,对应数组尾部
removeElementAt(len-1); //移除当前栈顶元素
return obj; //返回移除的栈顶元素
}*/
// 移除方法removeElementAt学习
/*public synchronized void removeElementAt(int index) {
modCount++; // 修改计数
// 数组越界处理,对外抛出异常
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);
}else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
// 计算当前index后面的元素个数
int j = elementCount - index - 1;
if (j > 0) {
// 借助系统帮助类对数组进行重组
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null;// 赋值为null交给垃圾回收处理
}*/
}
}
import java.util.LinkedList;
import java.util.Queue;
/**
* @author Administrator
*
* @description 队列的简单测试代码
* @history
*/
public class QueueTest {
/**
*@description
*@param args
*/
public static void main(String[] args) {
// 采用LinkedList类进行简单模拟功能
// public class LinkedList implements Deque{}
// 队列接口继承自collection接口
// public interface Queue extends Collection {}
Queue queue = new LinkedList();
// 入队列操作
queue.add("hello");
queue.add("world");
// 出队列操作
while(!queue.isEmpty()){
System.out.print(queue.poll()+" "); // 队列特点先进先出、hello world
}
// LinkedList源代码初步学习
/* 定义了一个十分常见的Entry类在HashMap源代码中也看到过
private static class Entry { // 静态内部类
E element; // 泛型
Entry next; // 后继结点引用
Entry previous; // 前驱结点引用
// 构造方法、构建对象时候初始化操作
Entry(E element, Entry next, Entry previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}*/
// LinkedList类中add方法(队列入队操作)
/*private transient Entry header = new Entry(null, null, null);
*
public LinkedList() {
//new LinkedList();操作初始化设置为null
header.next = header.previous = header;
}
// add方法实现
public boolean add(E e) {
// addBefore方法调用,处理queue.add("hello");方法调用add
addBefore(e, header);
return true;
}
// addBefore(e,header)方法 header链表维护关系参数
private Entry addBefore(E e, Entry entry) {
// 采用的前驱后继引用方式存储
Entry newEntry = new Entry(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
// poll方法实现类似add从header头部取出元素即可
*/
}
}
第四章:串
一、串的定义
计算机上的非数值处理的对象基本上都是字符串数据。串是由零个或多个字符组成的有限序列。串中字符的数目成为字符串的长度,零个字符的串成为空串。串的模式匹配算法经典的是KMP算法。
二、研究String类
在java编程语言中提供了String类,下面通过代码简单对该类进行学习。
[color=red] 学习过程中的代码如下:[/color]
/**
* @author Administrator
*
* @description String类学习测试类
* @history
*/
public class StringTest {
/**
*@description
*@param args
*/
public static void main(String[] args) {
// java应用程序有个概念编译期和运行期
// 编译期能够确定的字符串常量、运行期有个常量池的概念
String str1 = "hello world";
String str2 = "hello world";
System.out.println(str1==str2); //true
// ==运算符当比较基本数据类型的时候比较的是值大小是否相等,其他比较的是对象在内存的地址值
// equals方法是从根基类Object中继承来的,String类对其进行了覆写,比较的是内容是否相同
String str3 = new String("hello world");
System.out.println(str3==str2); //false
System.out.println(str2.equals(str3)); // true
// string类常用的方法,不要强记,开发看看API就好
String str = "hello2world";
System.out.println(str.charAt(0)); //h
System.out.println(str.substring(0, str.length())); //hello2world
String[] strArray = str.split("2"); // 截取操作
for(int i=0;i System.out.print(strArray[i]+" ");// hello world
}
// String类和StringBuffer和StringBuilder类的用法差别
// StringBuffer类是使用缓冲期的可变字符串,效率比不可变的String类更快,根据具体场合选择使用
// StringBuffer和StringBuilder类的区别在于方法是否同步的,这个也看具体的场合选择使用
StringBuffer sb = new StringBuffer("hello");
sb.append("world"); // StringBuffer的append方法比较常用
System.out.println(sb.toString()); // toString方法的调用
}
}
第五章:数组和广义表
一、数组和广义表定义
数组是读者已经很熟悉的一种数据类型,几乎所有的程序设计语言都把数组类型设为固有的类型。数组的应用中涉及到一个比较重要的数学知识,矩阵的压缩存储问题。广义表是线性表的推广,在java开发中好像用得不多,有时间再进一步学习。