数据结构篇一
1.1数据结构概述
**数据结构的定义:数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结
构可以带来更高的运行或者存储效率。(数据结构往往同高效的检索算法和索引技术有关)。
数据:是人们利用文字符号、数字符号以及其他规定的符号对事物的抽象描述。
数据元素:描述事物的一组数据成为数据元素,构成数据元素的数据成为数据元素的数据项。
抽象数据元素与具体数据元素:没有实际含义的数据元素称为抽象数据元素,描述事物的特性等有实际意义的元素称为
具体数据元素.
1.2数据结构的逻辑结构
定义 :数据元素之间的相互联系方式即为数据结构的逻辑结构
分类:数据结构主要分为线性结构、树状结构、图形结构三种。如下图所示
(1)线性结构
(2)树形结构
(3)图形结构
线性结构:除了第一个数据元素和最后一个元素外,每一个元素只有唯一的前驱数据元素和
唯一的后继数据元素;
树状结构:出根节点外,每一个数据元素只有唯一的前驱元素,可以有零个或若干个后
继元素;(非线性结构)
图形结构:每一数据元素只有零个或若干个前驱数据元素和零个或若干个后继数据元素
(非线性结构)
1.3数据的存储结构
数据的存储结构:数据元素在计算机中的存储方式,可分为 **顺序存储结构**和**链式存储结构**。
顺序存储结构:数据元素存储与一块指定的连续地址空间内存中,特点是逻辑上相邻的数据元素在物理结构上也是相邻
的。
链式存储结构:使用指针把相互直接关联的结点(前驱结点和后继结点)链接起来,
指针指向的是物理存储单元地址的变量,**结点**是将数据元素域和指针域组成的结构体。
如图所示
(1)顺序存储结构
(2)链式存储结构
常见的数据结构
数组(Array)
数组是一种聚合数据类型,它是将具有相同类型的若干变量有序地组织在一起的集合。数组可以说是最基本的数据结构,在各种编程语言中都有对应。一个数组可以分解为多个数组元素,按照数据元素的类型,数组可以分为整型数组、字符型数组、浮点型数组、指针数组和结构数组等。数组还可以有一维、二维以及多维等表现形式。
栈( Stack)
栈是一种特殊的线性表,它只能在一个表的一个固定端进行数据结点的插入和删除操作。栈按照后进先出的原则来存储数据,也就是说,先插入的数据将被压入栈底,最后插入的数据在栈顶,读出数据时,从栈顶开始逐个读出。栈在汇编语言程序中,经常用于重要数据的现场保护。栈中没有数据时,称为空栈。
队列(Queue)
队列和栈类似,也是一种特殊的线性表。和栈不同的是,队列只允许在表的一端进行插入操作,而在另一端进行删除操作。一般来说,进行插入操作的一端称为队尾,进行删除操作的一端称为队头。队列中没有元素时,称为空队列。
链表( Linked List)
链表是一种数据元素按照链式存储结构进行存储的数据结构,这种存储结构具有在物理上存在非连续的特点。链表由一系列数据结点构成,每个数据结点包括数据域和指针域两部分。其中,指针域保存了数据结构中下一个元素存放的地址。链表结构中数据元素的逻辑顺序是通过链表中的指针链接次序来实现的。
树( Tree)
树是典型的非线性结构,它是包括,2个结点的有穷集合K。在树结构中,有且仅有一个根结点,该结点没有前驱结点。在树结构中的其他结点都有且仅有一个前驱结点,而且可以有聊个后继结点,m≥0。
图
图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。如果两个顶点之间存在一条边,那么就表示这两个顶点具有相邻关系。
堆(Heap)
堆是一种特殊的树形数据结构,一般讨论的堆都是二叉堆。堆的特点是根结点的值是所有结点中最小的或者最大的,并且根结点的两个子树也是一个堆结构。
散列表(Hash)
散列表源自于散列函数(Hash function),其思想是如果在结构中存在关键字和T相等的记录,那么必定在F(T)的存储位置可以找到该记录,这样就可以不用进行比较操作而直接取得所查记录。
2线性表
定义:线性表是一种可以在任意位置进行插入和删除数据元素操作的,由N(N>0)个相同类型的数据元素组成的线性集合
线性表的顺序存储结构又称为顺序表,实现顺序存储结构的方法是使用数组。
数组又分为静态数组和动态数组两种,静态数组与动态数组的区别在与,静态数组申请和释放存储空间由系统自动完成,动态数组存储空间的申请和释放由用户通过用户调用系统函数自己完成
2.1常见的线性表
2.1.1动态数组
动态数组:
package com.opeanlab.线性表;
public class ArrayList<E> implements List<E>{
private static int DEFAULT_SIZE=10;
//用顺序存储结构实现的List-顺序线性表-顺序表
private E[] data;//存储数据的容器 E是抽象的东西
private int size; //线性表的有效元素的个数
// data.length表示线性表的最大容量Capacity
public ArrayList(){
this(DEFAULT_SIZE);
}
public ArrayList(int Capacity){
this.data=(E[])new Object[Capacity];
this.size=0;
}
/*
* 将一个数组封装成为一个线性表
*
* */
public ArrayList(E[] arr){
data=(E[])new Object[arr.length];
for(int i=0;i<data.length;i++){
data[i]=arr[i];
}
size=data.length;
}
//获取当前数组的有效元素
public int getSize() {
return size;
}
//判断当前数组是否为空
public boolean isEmpty() {
return size==0;
}
//根据角标向数组中插入元素
public void add(int index,E e) {
if(index<0||index>size){
throw new ArrayIndexOutOfBoundsException("角标不合法");
}
//当数组装满元素时 进行扩容
if(size==data.length){
resize(2*data.length);
}
for(int i=size-1;i>=index;i--){
data[i+1]=data[i];
}
data[index]=e;
size++;
}
//重新定义数组长度 并将原数组中的元素进行迁移
private void resize(int newLen) {
E[] arr=(E[])new Object[newLen];
for(int i=0;i<data.length;i++){
arr[i]=data[i];
}
data=arr;
}
//在当前数组的头部添加元素
public void addFirst(E e) {
add(0,e);
}
//在数组的尾部进行插入元素
public void addLast(E e) {
add(size,e);
}
//根据角标获取元素
public E get(int index) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("get函数角标越界");
//抛出数组角标越界异常
}
return data[index];
}
//获取第一个元素
public E getFirst() {
return get(0);
}
//获取最后一个元素
public E getLast() {
return get(size-1);
}
//根据角标修改元素
public void set(int index, E e) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("角标不合法");
}
data[index]=e;
}
//判断数组中是否包含指定元素
public boolean contains(E e) {
if(isEmpty()){
return false;
}
for(int i=0;i<size;i++){
if(data[i]==e){
return true;
}
}
return false;
}
//在数组查找元素返回元素的角标
public int find(E e) {
if(isEmpty()){
return -1;
}
for(int i=0;i<size;i++){
if(data[i]==e){
return i;
}
}
return -1;
}
//根据角标删除元素
public E remove(int index) {
if(index<0||index>size-1){
throw new ArrayIndexOutOfBoundsException("remove角标不合法");
}
E e=get(index);
for(int i=index+1;i<size-1;i++){
data[i-1]=data[i];
}
size--;
//缩容条件
if(data.length>DEFAULT_SIZE && size==data.length/4){
resize(data.length/2);
}
return e;
}
//删除第一个元素
public E removeFirst() {
return remove(0);
}
//删除最后一个元素
public E removeLast() {
return remove(size-1);
}
//删除指定元素
public void removeElement(E e) {
int index=find(e);
if(index==-1){
throw new IllegalArgumentException("删除元素不存在");
}
remove(index);
}
//清空数组
public void clear() {
this.size=0;
}
//获取数组的容量
public int getCapacity1(){
return data.length;
}
//打印当前数组
public String toString(){
//StringBuffer 线程安全 StringBuilder 线程不安全
StringBuilder sb=new StringBuilder();
sb.append("ArrayList:size="+size+"Capcity="+data.length+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
for(int i=0;i<size;i++){
sb.append(data[i]);
if(i==size-1){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
//交换数组中两个元素的位置
public void swap(int i,int j){
//断言
/*assert i>=0&&i<=size-1;
assert j>=0&&j<=size-1;*/
E e=data[i];
data[i]=data[j];
data[j]=e;
}
}
栈:用动态数组数组实现的栈,其特点先进后出,添加元素与删除元素都是从为不进行插入和删除的。
package com.opeanlab.栈;
import com.opeanlab.线性表.ArrayList;
public class ArrayStack<E> implements Stack<E> {
private ArrayList<E> list;
/**
*
* 创建一个默认无参的构造函数
* */
public ArrayStack(){
list=new ArrayList<E>();
}
/**
*
*
*
* */
public ArrayStack(int Capacity){
list=new ArrayList<E>(Capacity);
}
//当前栈的有效元素
public int getSize() {
return list.getSize();
}
//判断栈是否为空
public boolean isEmpty() {
return list.isEmpty();
}
//向栈中添加元素
public void push(E e) {
list.addLast(e);
}
//出栈元素(删除元素)
public E pop() {
return list.removeLast();
}
//查看当前栈顶元素
public E peek() {
return list.getLast();
}
//清空栈
public void clear() {
list.clear();
}
//比较两个栈中的元素是否相同
public boolean equals(Object obj){
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayStack){
ArrayStack stack = (ArrayStack) obj;
return list.equals(stack.list);//私有化在内部可以调用,在外部不可调用
}
return false;
}
//打印栈中元素
public String toString(){
StringBuilder sb=new StringBuilder();
sb.append("ArrayStack size="+getSize()+",Capasity="+list.getCapacity());
if(isEmpty()){
sb.append("[]");
}else{
sb.append("[");
for(int i=0;i<getSize();i++){
sb.append(list.get(i));
if(i==getSize()-1){
sb.append("]");
}else{
sb.append(",");
}
}
}
return sb.toString();
}
}
队列:一种特殊的线性表,添加元素只能从尾部添加,删除元素只能从头部删除,循环队列则是一种特殊的队列,添加元素和删除元素复杂度O(1);而且避免了空间的浪费,首先顶一个队列的接口,然后通过一个类实现该接口的方法
循环队列的代码如下
package com.openlab.队列01;
public class ArrayQueueLoop<E> implements Queue<E> {
private E[] data;//重新定义一个数组
private int size;//定义一个数组有效元素个数
private int front;//定义一个头指针
private int rear;//定义一个尾指针
private static final int DEFAULT_SIZE=11;//定义数组的默认容量大小
//创建一个无参构造函数进行初始化
public ArrayQueueLoop(){
this(DEFAULT_SIZE);
}
//创建一个有参的构造函数
public ArrayQueueLoop(int capacity){
this.data=(E[])new Object[capacity+1];
this.rear=0;
this.front=0;
this.size=0;
}
@Override
//获取当前队列中的有效元素个数,返回size
public int getSize() {
return size;
}
@Override
//判断队列是否为空,条件有效元素个数为0,且头尾指针指向同一位置
public boolean isEmpty() {
return size==0&&rear==front;
}
@Override
//清空队列,有效元素个数等于0;头尾指针指向0,并创建一个新数组赋给data
public void clear() {
data=(E[])new Object[DEFAULT_SIZE];
size=0;
rear=0;
front=0;
}
@Override
//进队一个元素,判断当前队列是否已经满了,没满执行data[rear]=e,更新尾指针,有效元素个数加1,队列已满则进
//行扩容
public void enQueue(E e) {
if(((rear+1)%data.length)==front){
resize(data.length*2-1);
}
data[rear]=e;
rear=(rear+1)%data.length;
size++;
}
@Override
//出队一元素,首先判断队列是否为空,如果队列为空抛出异常,不为空则进行头部删除元素,
public E deQueue() {
if(isEmpty()){
throw new IllegalArgumentException("循环队列为空,无法完成出对操作");
}
E e=data[front];
front=(front+1)%data.length;
size--;
//判断有效元素个数是否是数组容量的四分之一,且数组容量大于默认值,如果二者都满足则进行缩容
if(size<=data.length/4&&data.length>DEFAULT_SIZE){
resize(data.length/2);
}
return e;
}
//重新定义数组长度,并进行元素的迁移,最终将创建的数组赋给data,并更新头尾指针
private void resize(int newLen) {
E[] newData=(E[]) new Object[newLen];
int index=0;//将index赋给rear指针
for(int i=front;i!=rear;i=(i+1)%data.length){
newData[index++]=data[i];
}
front=0;
rear=index;
data=newData;
}
@Override
//获取队首元素
public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("循环队列为空,无法获取队首元素!");
}
return data[front];
}
@Override
//获取队尾元素
public E getRear() {
if(isEmpty()){
throw new IllegalArgumentException("循环队列为空,无法获取队尾元素!");
}
return data[(data.length+rear-1)%data.length];
}
//打印队列中的元素
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("ArrayQueueLoop size="+size+",Capacity="+(data.length-1));
//判断队列是否为空,如果为空返回“【】”
if(isEmpty()){
return "[]";
}else{
sb.append("[");
//从队首元素开始打印
for(int i=front;i!=rear;i=(i+1)%data.length){
sb.append(data[i]);
if(i==(rear-1)%data.length){
sb.append("]");
}else{
sb.append(",");
}
}
}
return sb.toString();
}
}