线性表之顺序存储结构与链式存储结构 及 应用

前言

我们常用的线性表是顺序存储结构和链式存储结构表示,是最基本、最简单、也是最常用的一种数据结构;一个线性表是由n个相同特性的数据的有限序列;比如java中的数组 ,链表;所以学习这两种结构表示是非常必要的。

线性表结构特点

  • 均匀性  数据元素是不同的,对于同一线性结构的各种数据元素必定有相同的数据类型和长度
  • 有序性 各数据元素在线性表中的位置只取决于它们的序号,数据元素之前的相对位置是线性的,即存在唯一的“第一个“和“最后一个”的数据元素,除了第一个和最后一个外,其它元素前面均只有一个数据元素(直接前驱)和后面均只有一个数据元素(直接后继)。

顺序存储结构

定义

用一组地址连续的存储单元依次存储线性表的结构,它以“物理位置相邻”来表示线性表中数据元素间的逻辑关系,可随机存取表中任一元素。

  • a1是a2的前驱,ai+1是ai的后继,a1没有前驱,an没有后继,n为线性表的长度,若n==0时,则线性表为空,这个长度n也就是我们平常size的概念,每个线性表,一定会有size的概念‘
  • 这里还有个注意点,比如arraylist中实现,不要只局限于基本数据类型,可以存object类型的 ,这下面的
class ArrayList{
Student[40];
int size;
}

比如上图的结构也是顺序结构

插入与删除

插入某个元素,一定是会 将后面数据进行移动 复制, 就像  arraylist中  下面进行插入

     System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);

 

删除某个元素,也是需要 将后面元素进行复制

 

优缺点

优点:
   尾插效率高,支持随机访问。
缺点:
   中间插入或者删除效率低。
应用:
  数组
   ArrayList  

利用顺序结构的蛮力排序(蛮力法)

针对数据量少的排序方式 ,速度快;但数据量大了过后,时间会降低;

 蛮力法(brute force method,也称为穷举法或枚举法)
是一种简单直接地解决问题的方法,
常常直接基于问题的描述,
所以,蛮力法也是最容易应用的方法。
但是,用蛮力法设计的算法时间特性往往也是最低的,
典型的指数时间算法一般都是通过蛮力搜索而得到的 。(即输入资料的数量依线性成长,所花的时间将会以指数成长)

这种算法,在数据量很少的情况下,速度是最快的;也就是我们常说的冒泡法和快速排序法;

应用:数据量足够小,比如斗牛游戏的牌面排序 ;也就是 n一般小于5的情况

冒泡排序算法

第一轮进行 只要当前点大于(或者小于)下个结点,就进行交换,就能取到一个最大(最小)的结点,这样不断循环

 

实现代码

 for(int i=array.length-1;i>0;i--) {
            boolean flag=true;
            for (int j = 0; j < i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag=false;
                }
            }
            if(flag){
                break;
            }
        }
  • 上面的代码 分为第一轮,首先 冒泡到 array.length-1
 for(int i=array.length-1;i>0;i--) {
  • 第二轮   需要在剩余的 不断交换
 for (int j = 0; j < i; j++) {
  • 这样的时间复杂度 的就是 n(n-1)/2  ,  具体就是  n ,n-1,n-2.....1  相加
  •  针对小数据量,做了个flag进行优化, 数据已经排列好时,就先退出不走后面逻辑

 

这里做的一个 花色和点数进行比较排序数据 利用对象进行排序

  • 首先做一个 cards 需要排序的 方式 利用comparable ;
public class Cards implements Comparable{
    public int pokerColors;//花色
    public int cardPoints;//点数

    public Cards(int pokerColors, int cardPoints) {
        this.pokerColors = pokerColors;
        this.cardPoints = cardPoints;
    }
    //提供一个方法,用来比较对象的大小
    @Override
    public int compareTo(@NonNull Object o) {
        Cards c=(Cards)o;
        if(this.cardPoints>c.cardPoints){
            return 1;
        }else if(this.cardPoints<c.cardPoints){
            return -1;
        }
        if(this.pokerColors>c.pokerColors){
            return 1;
        }else if(this.pokerColors<c.pokerColors){
            return -1;
        }
        return 0;
    }

    @Override
    public String toString() {
        return "Cards{" +
                "pokerColors=" + pokerColors +
                ", cardPoints=" + cardPoints +
                '}';
    }

}
  • 进行冒泡排序数据
  for(int i=array.length-1;i>0;i--) {
            boolean flag=true;
            for (int j = 0; j < i; j++) {
                if (array[j].compareTo(array[j+1])>0) {
                    Cards temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag=false;
                }
            }
            if(flag){
                break;
            }
        }

  冒泡法应用场景,在数据很多已经排序完成,则使用冒泡效率是非常高的

选择排序算法

该算法是快排的基础,理解快排,一定要理解选择排序;

 第一轮进行 选择一个基准结点一般为0结点,遍历数组,一旦遇到比他小(大)进行指针下标交换,第一轮下来,就能找到最小的点的指针,然后与基准点进行交换,基准点就能找到最小的数据

具体的代码

    public static void selectSort(int[] array){
        for(int i=0;i<array.length-1;i++) {
            int index = i;
            for (int j = i+1; j < array.length; j++) {
                if (array[j] < array[index]) {
                    index = j;
                }
            }
            //{1,2,5,8,3,9,4,6,7};
            if(index!=i) {//如果已经是最小的,就不需要交换
                int temp = array[index];
                array[index] = array[i];
                array[i] = temp;
            }
        }
    }

针对蛮力法的应用,一般是小于5的数据可以使用,如果数据量大,不使用该种算法

链表式存储结构

线性表下定义链式存储结构

种类

  • 单链表

单链表中基本单位为节点,而每个节点 分为数据域和指针域;而指针域指向的是下个节点的下标;数据域则可以代表很多东西 包括数据,还有其他属性

 针对插入和删除节点的情况

这就是单向链表的删除和添加节点的过程

单链表的应用(链式奇数排序对麻将进行排序)

解决思路

创建 对应的麻将对象

public class Mahjong {
    public int suit;//筒,万,索
    public int rank;//点数 一  二  三

    public Mahjong(int suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }

    @Override
    public String toString() {
        return "("+this.suit+" "+this.rank+")";
    }
}

需要分别按花色和大小进行排序

      //先对点数进行分组
        LinkedList[] rankList=new LinkedList[9];
        for (int i=0;i<rankList.length;i++){
            rankList[i]=new LinkedList();
        }
  • 第一次处理按大小 分成9个点数分为九段链表

 //把数据一个一个的放入到对应的组中
        while(list.size()>0){
            //取一个
            Mahjong m=list.remove();
            //放到组中
            rankList[m.rank-1].add(m);
        }
        //把9个组合到一起
        for (int i = 0; i < rankList.length; i++) {
            list.addAll(rankList[i]);
        }

 

  • 第二次按花色进行拆分

//先花色数进行分组
        LinkedList[] suitList=new LinkedList[3];
        for (int i=0;i<suitList.length;i++){
            suitList[i]=new LinkedList();
        }
        //把数据一个一个的放入到对应的组中
        while(list.size()>0){
            //取一个
            Mahjong m=list.remove();
            //放到组中
            suitList[m.suit-1].add(m);
        }
        //把3个组合到一起
        for (int i = 0; i < suitList.length; i++) {
            list.addAll(suitList[i]);
        }

先可以按花色在按点数进行分组,都是可以的,主要是按单链表顺序排序的结构

 

  • 双链表

 

这里的查找删除,主要是可以借助于linkedlist的源码,也是双向链表的

 

/**
	 * 在最后添加
	 * @param e
	 */
	private void linkLast(E e) {
		Node<E> newNode = new Node<E>(last, e, null);
		Node<E> l = last;

		last = newNode;

		if (l == null) {
			first = newNode;
		}else {
			l.next = newNode;
		}
		size ++;
	}

 

/**
	 * �在index的位置上添加一个元素
	 * @param index
	 * @param e
	 */
	public void add (int index, E e) {
		if(index < 0 || index >size) {
    		return;
    	}
		if (index == size) {
			linkLast(e);
		} else {
			Node<E> target = node(index);
			Node<E> pre = target.prev;
			Node<E> newNode = new Node<E>(pre, e, target);

//			pre.next = newNode;
//			pre = newNode;
			//要考虑index=0时的情况
			if(pre == null) {
				first = newNode;
			} else {
				pre.next = newNode;
			}
			pre = newNode;
			size++;
		}
	}
	

主要注意点,在添加 删除节点时,和单链表的区别 ,要考虑 前后节点都要修改

参考分析 可以看看我的linkedlist的源码分析

Java 集合深入理解 (二) :LinkedList链表源码研究,及双向队列如何实现

  • 单向循环链表

    和单向循环链表就是,后指针指向前面个

  • 双向循环链表

而对于双向循环链表,则 指针修改的

 

总结

整个线性表的两种数据结构,各有各的应用场景,各有优缺点,主要在我们java中可以使用到,我们在应用中,各个场景下实现。

 

 

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踩踩踩从踩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值