一、线性表简介
元素与元素之间具有一对一的对应关系。
- 概念:
线性表是数学应用在计算机科学中的一种简单与基本的数据结构,是n个相同类型的元素的有限序列(n>=0)。它的关系可以看成是一种有序对的集合,目的在于表示线性表中任意两个相邻元素之间的关系。其中ai-1称为ai的先行元素,ai是ai-1的后继元素,可以表示为:(a1,a2,a3,an-1,……,an)。
- 线性表有以下特点:
1、有序表可以是空集合。
2、存在唯一的第一个元素a1与唯一的最后一个元素an。
3、除第一个元素外,每一个元素都有唯一的先行者。
4、除最后一个元素外,每一个元素都有唯一的后继者。
- 线性表的基本运算:
1、置空表initlist(L),构造一个空的线性表;
2、求表长listLength(L),返回线性表的长度;
3、取元素getNode(L,i),1<=i<=n;
4、按值查找 LocateNode(L,x),返回第一个为X的结点的位置,若表中不存在则返回0
5、插入insert(L,i,x) 在L的i位置插入x,
6、删除delete(L,i) 删除表中的第i个元素
- 线性表的存储结构
静态数据结构:也称为“密集表”,它使用连续分配的内存空间来存储数据,在建立初期,必须事先声明最大可能要占用的固定内存空间。
优点:设计简单,易读取。如:数组
缺点:删除或加入数据困难。因为需要移动大量的数据。
动态数据结构:又称为“链表”,使用不连续的内存空间存储数据,内存在程序执行时才进行分配,不需要事先声明。
优点:能充分节省空间。
缺点:设计麻烦;读取麻烦。
- 顺序表与链表的对比
二、线性表的实现
- 顺序表
实现思路:
1、 顺序表因为采用顺序存储形式,所以内部用数组来存储数据。
2、因为存储的具体对象类型不一定,所以采用泛型操作
数组操作的优缺点:
优点:
通过指针快速定位到表中,查询快速
缺点:
1.数组声明时即需要确定数组大小。当操作中超过容量时,则需要重新声明数组,并且复制当前所有数据。
2.当需要在中间进行插入或者删除时,则需要移动大量元素(size-index个)。
定义接口:
package com.zm.linear.list;
/*现性表(列表)的接口定义*/
public interface MyList {
//新增一个元素
void add(Object element);
//删除相同的元素
void delete(Object element);
//根据索引删除元素
void delete(int index);
/**将指定索引位置的元素替换成新的元素
* @param index
* @param newElement
* */
//清空数组
void deleteAll();
void update(int index,Object newElement);
/***
* @param target
* @return
*/
boolean contains(Object target);
/**
* 返回指定索引处的元素
* @param index
* @return
*/
Object at(int index);
/**
* 查找指定元素的索引
* @param element
* @return
*/
int indexOf(Object element);
}
具体实现:
package com.zm.linear.list;
public class MyArrayList implements MyList {
//真正存储元素的底层结构
private Object[] elements;
//元素个数
private int size = 0;
//容量
private int capacity = 10;
public MyArrayList() {
//创建一个数组对象
elements = new Object[capacity];
}
public MyArrayList(int capacity) {
this.capacity = capacity;
elements = new Object[capacity];
}
@Override
public void add(Object element) {
//扩容
if (size == capacity) {
capacity *= 2;//增加一倍的容量
//新建一个数组
Object[] newArr = new Object[capacity];
for (int i = 0; i < size; i++) {
newArr[i] = elements[i];
}
//用新建的数组替换原来的数组
elements = newArr;
}
//向数组中添加值
elements[size++] = element;
}
//删除指定元素
@Override
public void delete(Object element) {
int index=indexOf(element);
if(index >= 0) {
delete(index);
delete(element);
}
}
//删除指定位置的元素
@Override
public void delete(int index) {
if(!(index<0||index>elements.length)){//如果指定的位置在数组之中
//--size,说明数组中的元素个数减了1
Object[] newArr = new Object[--size];
//此处size的值已经变为了2
for(int i=0;i<size;i++){
if(i<index){
newArr[i]=elements[i];
}else{
newArr[i]=elements[i+1];
}
}
elements=newArr;
}
}
@Override
public void deleteAll() {
for(int i = 0;i<size;i++){
elements[i]=null;
}
}
@Override
public void update(int index, Object newElement) {
elements[index] = newElement;
}
@Override
public boolean contains(Object target) {
return indexOf(target)>0;
}
@Override
public Object at(int index) {
return elements[index];
}
@Override
public int indexOf(Object element) {
for(int i=0;i<size;i++){
if(elements[i].equals(element)){
return i;
}
}
return -1;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < size; i++) {
sb.append(elements[i] + (i == size-1 ? "" : ","));
}
sb.append("]");
return sb.toString();
}
}
测试:
package com.zm.linear.list;
import org.junit.Before;
import org.junit.Test;
public class MyListTest {
MyArrayList m = new MyArrayList();
//向容器中添加元素
@Test
@Before
public void addTest(){
m.add("addas");
m.add("tebu");
m.add("balabala");
m.add("addas");
m.add("tebu");
System.out.println(m);
}
//删除指定元素
@Test
public void delElementTest(){
m.delete("balabala");
System.out.println(m);
}
//修改数组中的元素
@Test
public void updateTest(){
m.update(2,"aaaaaaaa");
System.out.println(m);
}
//清空数组
@Test
public void deleteAllTest(){
m.deleteAll();
System.out.println(m);
}
}
二、 链表
(一)单链表
定义节点:
package com.zm.linear.list;
//链表节点
public class ListNode {
Object data;//数据
ListNode next;//后继
public ListNode() {
}
public ListNode(Object data) {
this.data = data;
}
}
package com.zm.linear.list;
public class SingleLinked implements MyList{
/*定义链表头节点,链表必须有头节点*/
private ListNode head;
/*定义尾节点*/
private ListNode last;
//记录链表中元素的个数
private int size;
@Override
public void add(Object element) {
if(head==null){//如果头节点为空,则创建一个头节点
head = new ListNode(element);
last = head;
}else{//如果头节点不为空,则添加一个节点
//要使next可见,可以修改next的可见性或set/get方法
last.next = new ListNode(element);
last = last.next;
}
size++;
}
@Override
public void delete(Object element) {
//头指针本身不能动
ListNode p = head;
//记录上一次比对的节点
ListNode pre=null;
while(p!=null){
if(p.data.equals(element)){
if(p==head){//如果要删除第一个元素,需要移动头指针
head=head.next;
}else{
pre.next=p.next;
break;
}
size--;
}
pre=p;
p=p.next;
}
}
@Override
public void delete(int index) {
if(index<0||index>=size){
return;
}
int i=0;//指针指向的节点的索引
ListNode p=head;
ListNode pre=null;
while (p!=null){
if(i==index){//此时p就指向要删除的那个节点
if(p==head){
head=head.next;
}else{
pre.next=p.next;
}
}
pre=p;
p=p.next;
i++;
}
size--;
}
@Override
public void deleteAll() {
}
@Override
public void update(int index, Object newElement) {
int i=0;//指针指向的节点的索引
ListNode p= head;
while(p!=null){
if(i==index){//如果现在指针指向的索引与需要修改的索引相同
p.data=newElement;
}
p = p.next;//移动指针
i++;
}
}
//判断是否包含该元素
@Override
public boolean contains(Object target) {
ListNode p=head;
while (p!=null){
if(p.data.equals(target)){
return true;
}
p=p.next;//移动指针
}
return false;
}
//给定一个索引,返回节点内容
@Override
public Object at(int index) {
if(index<=0||index>=size){
return null;
}
int i=0;//指针指向的节点的索引
ListNode p = head;
while (p!=null){
if(i==index){
return p.data;
}
p=p.next;
i++;
}
return null;
}
@Override
public int indexOf(Object element) {
int i=0;//记录指针指向的节点的索引
ListNode p=head;
while(p!=null){
if(p.data.equals(element)){
return i;
}
p=p.next;
i++;
}
//如果没有找到,返回-1
return -1;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
ListNode p = head;
while (p!=null){
sb.append(p.data);
if(p.next!=null){
sb.append(",");
}
p=p.next;//移动p的指针
}
sb.append("]");
return sb.toString();
}
}
测试方法:
package com.zm.linear.list;
import org.junit.Before;
import org.junit.Test;
public class MyListTest {
MyArrayList m = new MyArrayList();
//向容器中添加元素
@Test
@Before
public void addTest(){
m.add("addas");
m.add("tebu");
m.add("balabala");
System.out.println(m);
}
//删除指定元素
@Test
public void delElementTest(){
m.delete("balabala");
System.out.println(m);
}
//修改数组中的元素
@Test
public void updateTest(){
m.update(2,"aaaaaaaa");
System.out.println(m);
}
//清空数组
@Test
public void deleteAllTest(){
m.deleteAll();
System.out.println(m);
}
}
(二)双向链表
package com.zm.linear.list;
//链表节点
public class ListNode {
Object data;//数据
ListNode next;//后继
ListNode pre;//前驱
public ListNode() {
}
public ListNode(Object data) {
this.data = data;
}
}
package com.zm.linear.list;
public class DoubleLinkList implements MyList{
private ListNode head = new ListNode(null);//头节点
private ListNode last = new ListNode(null);//尾节点(亚元)
private int size;//记录链表中元素的个数
public DoubleLinkList() {
//头节点没有前驱,尾节点没有后继
head.next=last;
last.pre=head;
}
@Override
public void add(Object element) {
ListNode newNode = new ListNode(element);
last.pre.next=newNode;
newNode.next = last;
newNode.pre=last.pre;
last.pre=newNode;
size++;//添加一个节点
}
@Override
public void delete(Object element) {
//定义初始指针
ListNode p = head.next;
//初始化p的前驱
ListNode pre = null;
while(p != last){
if(p.data.equals(element)){
//前驱的后继指向现在的后继
p.pre.next=p.next;
p.next.pre=p.pre;
size--;
break;//找到了之后就跳出循环
}
pre = p;
p = p.next;
}
}
@Override
public void delete(int index) {
//判断所给的索引是否超出范围
if(index<0||index>=size){
return;//结束
}
int i=0;//记录索引值
ListNode p = head.next;
while(p != last){
if(i==index){
p.pre.next = p.next;
p.next.pre = p.pre;
size--;
break;
}
p = p.next;
i++;
}
}
@Override
public void deleteAll() {
}
@Override
public void update(int index, Object newElement) {
if(index<0||index>=size){
return;
}
int i = 0;
ListNode p = head.next;
while(p!=last){
if(i==index){
p.data = newElement;
break;
}
p = p.next;
i++;
}
}
@Override
public boolean contains(Object target) {
ListNode p = head.next;
while (p!=last){
if(p.data.equals(target)){
return true;
}
p = p.next;
}
return false;
}
@Override
public Object at(int index) {
if(index < 0 || index >= size){
return null;
}
ListNode p = head.next;
int i = 0;//记录索引
while(p!=last){
if(i==index){
return p.data;
}
p = p.next;
i++;
}
return null;
}
//通过传进来的元素找索引
@Override
public int indexOf(Object element) {
ListNode p = head.next;
int i = 0;//记录索引
while(p!=last){
if(p.data.equals(element)){
return i;
}
p = p.next;
i++;
}
return -1;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
ListNode p = head.next;
while(p!=last){//当p还没有指向最后一个节点
sb.append(p.data);
if(p.next!=last){
sb.append(",");
}
//移动p的指针
p=p.next;
}
sb.append("]");
return sb.toString();
}
}
package com.zm.linear.list;
import org.junit.Before;
import org.junit.Test;
public class SingleLinkedTest {
SingleLinked list = new SingleLinked();
//向容器中添加元素
@Test
@Before
public void addTest(){
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
list.add("g");
list.add("h");
System.out.println(list);
}
//删除指定元素
@Test
public void delElementTest(){
list.delete("c");
System.out.println(list);
}
//删除指定索引位置上的元素
@Test
public void deleteIndex(){
list.delete(0);
System.out.println(list);
}
//修改数组中的元素
@Test
public void updateTest(){
list.update(2,"aaaa");
System.out.println(list);
}
//清空数组
@Test
public void deleteAllTest(){
}
@Test
public void atTest(){
Object at = list.at(2);
System.out.println(at);
}
@Test
public void containsTest(){
boolean b = list.contains("a");
System.out.println(b);
}
@Test
public void indexOfTest(){
int index = list.indexOf("d");
System.out.println(index);
}
}