数据结构之绪论、线性表

绪论

数据结构的地位

在计算机相关专业的学习过程中,我们前期的课程主要是以学习语言基础为主,后期则是以软件架构设计,操作系统,软件工程相关的课程为主。而数据结构则是在中间起着承上启下的作用。

算法+数据结构=程序

数据结构的术语

数据  (Data)            可被计算机识别并加工处理的对象

数据元素(Element)            有数据组成的具有一定意义的基本单位

数据项(Item)         是组成数据元素的、不可分割的最小单位

数据对象(Data  Object)      具有相同性质的若干个数据元素的结合

         数据元素是组成数据的基本单位,数据项是组成数据元素的的基本单位,数据项是不可再分割的最小数据单位。但是在解决问题是,数据元素才是真正进行访问和处理的基本单位。

数据结构可以大致分为以下三块:

1、数据的逻辑结构

  • 逻辑结构的二元组表示为:B=(D,R)

        B:一种数据结构 , D:数据元素的集合, R:反应数据元素D之间的关系

  • 关系用序偶<x,y>表示

        x为第一元素,y为第二元素

        x为y的前驱元素,y为x的后继元素

数据的逻辑结构可以分为线性结构和非线性结构

  • 线性结构:数据元素之间存在一对一的关系 1:1

                         eg:线性表、栈、队列、串、数组······

  • 非线性结构:集合结构:数据元素除了同属于一个集合外,没有其他的关系     

                             eg:  S={  2,86,59,43,15}

                             树形结构:数据元素之间存在着一对多的关系    1:n

                             图结构:元素之间存在多对多的关系    m:n

2.数据的存储结构

存储结构(Storage  Structure):数据及数据之间的关系在计算机内的存储方式,这是面向程序员 

顺序存储结构(顺序存储)

对于顺序存储结构来说用下标来表示数据之间的关系

eg:数组

顺序存储结构中所有元素存放在一片地址连续的存储单元中

逻辑上相邻的元素在物理位置上也相邻,不需要额外空间表示元素之间的逻辑关系。

链式存储结构(随机存储)

数据元素存放在任意存储单元中,这组存储单元可以是连续的,也可以是不连续的。

通过指针域来反映数据元素的逻辑关系。

eg:单链表

head唯一标识单链表

通过指针域来反映数据元素的逻辑关系

索引存储结构

建立附加的索引来标识数据元素的地址。他不是独立的存储结构,只是为了在查找运算是,能够减少查找的时间,提高数据查找的性能。如:手机通讯录在存储的同时可以按字母索引。

散列存储结构

又称为Hash存储,根据结点的关键字直接计算出该结点的存储地址。它是一种能快速实现访问的存储地址。它是一种能快速实现访问的存储方式,在理想情况下,无需比较即可根据指定值直接定位记录的存储位置。如:缓存存储、快速查找等。

数据的运算

数据的运算是对数据的操作,分为两个层次:运算描述运算实现

最常见的运算:

  • 搜素运算:在数据结构中搜索满足一定条件的元素;
  • 插入运算:在数据结构中插入新元素;
  • 删除运算:将数据结构中指定元素删除;
  • 更新运算:将数据结构中指定元素更行为新的元素;

同样的运算,在不同的存储结构中,其实现过程是不同的。

  • 顺序结构的查找:下标
  • 链式存储结构的查找:指针域

数据结构和数据类型

抽象数据类型=逻辑结构+抽象运算

抽象数据类型暂不考虑计算机的具体存储结构和运算的具体实现。

抽象数据类型实质上,就是描述问题本身(与计算机无关)

算法描述

算法可以用在自然语言、流程图、程序设计语言或者伪代码描述。

当一个算法用程序设计语言描述时,便成为程序。

程序和算法的最大区别:程序可以是无穷的,而算法必须满足有穷性

算法的概念

算法是对特定问题求解方法和步骤的一种描述,是指令的有限序列。其中每个指令表示一个或多个操作。

算法的重要特性:

  • 有穷性:算法必须在执行有限步之后终止。
  • 确定性:算法的每条指令的有确切的定义,没有二义性。
  • 可行性:算法的每一条指令都足够基本。
  • 输入性:算法有零个或多个输入
  • 输出性:算法有一个或多个输出

算法与程序

程序=数据结构+算法

算法分析

事后分析统计方法:编写算法对应程序,统计其执行时间。

事前估算分析方法:撇开上述因素,认为算法的执行时间是问题规模n的函数。

时间复杂度的分许方法

  • 找出语句频度最大的那条语句作为基本语句
  • 计算基本语句的频度得到问题规模n的某个函数f(n)
  • 取其数量级用符合“o"得到

 时间复杂度是由嵌套最深层语句的频度决定的

线性表

线性表是典型的线性结构

顺序表

 相关的代码如下:

public class SqListClass<E> {       //顺序表泛型类
    final int initcapacity = 10;    //顺序表的初始容量(常量)
    public E[] data;                //存放顺序表中元素
    public int size;                //存放顺序表的长度
    private int capacity;           //存放顺序表的容量

    public SqListClass() {  //构造方法,实现data和length的初始化
        data = (E[]) new Object[initcapacity];  //强制转换为E类型数组
        capacity = initcapacity;
        size = 0;
    }

    //线性表的基本运算算法
    public void CreateList(E[] a) { //由a整体建立顺序表
        size = 0;
        for (E e : a) {
            if (size == capacity) { //出现上溢出时
                updateCapacity(2 * size);   //扩大容量
            }
            data[size] = e;
            size++;     //添加的元素个数增加1
        }
    }

    public void Add(E e) {      //在线性表的末尾添加一个元素e
        if (size == capacity) { //顺序表空间满时倍增容量
            updateCapacity(2 * size);
        }
        data[size] = e;
        size++; //长度增1
    }

    public int size() { // 求线性表长度
        return size;
    }

    public void Setsize(int nlen) {    //设置线性表的长度
        if (nlen < 0 || nlen > size) {
            throw new IllegalArgumentException("设置长度:n不在有效范围内");
        }
        size = nlen;
    }

    public E GetElem(int i) {    //返回线性表中序号为i的元素
        if (i < 0 || i > size - 1) {
            throw new IllegalArgumentException("查找:位置i不在有效范围内");
        }
        return (E) data[i];
    }

    public void SetElem(int i, E e) {    //设置序号i的元素为e
        if (i < 0 || i > size - 1) {
            throw new IllegalArgumentException("设置:位置i不在有效范围内");
        }
        data[i] = e;
    }

    public int GetNo(E e) { //查找第一个为e的元素的序号
        int i = 0;
        while (i < size && !data[i].equals(e)) {
            i++;            //查找元素e
        }
        if (i >= size) {    //未找到时返回-1
            return -1;
        } else {
            return i;       //找到后返回其序号
        }
    }

    public void swap(int i, int j) {    //交换data[i]和data[j]
        E tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }

    public void Insert(int i, E e) {    //在线性表中序号i位置插入元素e
        if (i < 0 || i > size) {        //参数错误抛出异常
            throw new IllegalArgumentException("插入:位置i不在有效范围内");
        }
        if (size == capacity) {         //满时倍增容量
            updateCapacity(2 * size);
        }
        for (int j = size; j > i; j--) {    //data[i]及后面元素后移一个位置
            data[j] = data[j - 1];
        }
        data[i] = e;    //插入元素e
        size++;         //顺序表长度增1
    }

    public void Delete(int i) {         //在线性表中删除序号i位置的元素
        if (i < 0 || i > size - 1) {    //参数错误抛出异常
            throw new IllegalArgumentException("删除:位置i不在有效范围内");
        }
        for (int j = i; j < size - 1; j++) {    //将data[i]之后的元素前移一个位置
            data[j] = data[j + 1];
        }
        size--; //顺序表长度减1
        if (capacity > initcapacity && size == capacity / 4) {
            updateCapacity(capacity / 2);   //满足要求容量减半
        }
    }

    @Override
    public String toString() {    //将线性表转换为字符串
        String ans = "";
        for (int i = 0; i < size; i++) {
            ans += data[i].toString() + " ";
        }
        return ans;
    }

    private void updateCapacity(int newCapacity) {  //改变顺序表的容量为newCapacity
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i <size; i++) {
            newData[i] = data[i];
        }
        capacity = newCapacity; //设置新容量
        data = newData;         //仍由data标识数组
    }
}

单链表

相关代码如下:

package Date;

class LinkNode<E>{
    E data;
    LinkNode<E>next;
    public LinkNode(){
        next=null;
    }
    public LinkNode(E d){
        data=d;
        next=null;
    }
}
package Date;


public class LinkListClass<E> {//链表泛型类
    public LinkNode<E> head;//存放头结点

    public LinkListClass() {//构造方法
        this.head = new LinkNode<E>();//创建头结点
        this.head.next = null;
    }
    //线性表的基本运算算法
    public  void  CreateListF(E[] a){//头插法
        LinkNode<E> s;
        for (int i = 0;i<a.length;i++){
            s = new LinkNode<E>(a[i]);
            s.next = head.next;
            head.next = s;
        }

    }
    public void CreateListR(E[] a){
        LinkNode<E> s,t = head;
        for (int i=0;i<a.length;i++){
            s = new LinkNode<E>(a[i]);
            t.next = s;
            t=s;
        }
        t.next = null;
    }

    private LinkNode<E> geti(int i){
        LinkNode<E> p =head;
        int j = -1;
        while (j<i){
            j++;
            p=p.next;
        }
        return p;
    }
    public void Add(E e){
        LinkNode<E> s = new LinkNode<E>(e);
        LinkNode<E> p = head;
        while (p.next!=null){
            p=p.next;
        }
        p.next=s;
    }
    public  int size(){
        LinkNode<E> p = head;
        int cnt = 0;
        while (p.next!=null){
            cnt++;
            p=p.next;
        }
        return cnt;
    }
    public void Setsize(int nlen){
        int len = size();
        if (nlen<0||nlen>len){
            throw new IllegalArgumentException("设置长度:n不在有效范围内");

        }
        if (nlen==len){
            return;
        }
        LinkNode<E> p = geti(nlen-1);
        p.next = null;
    }
    public E GetElem(int i){
        int len = size();
        if(i<0||i>len-1){
            throw new IllegalArgumentException("查找:位置i不在有效范围内");

        }
        LinkNode<E> p = geti(i);
        return (E)p.data;

    }
    public void SetElem(int i,E e){
        if (i<0||i>size()-1){
            throw new IllegalArgumentException("设置:位置i不在有效范围内");

        }
        LinkNode<E> p = geti(i);
        p.data = e;

    }
    public int GetNo(E e){
        int j = 0;
        LinkNode<E> p = head.next;
        while (p!=null&&!p.data.equals(e)){
            j++;
            p=p.next;
        }
        if (p==null){
            return -1;
        }else {
            return j;
        }
    }
    public void swap(int i ,int j){
        LinkNode<E> p = geti(i);
        LinkNode<E> q = geti(j);
        E tmp = p.data;
        p.data = q.data;
        q.data = tmp;
    }

    public void Insert(int i ,E e){
        if (i<0||i>size()){
            throw new IllegalArgumentException("插入:位置i不在有效范围内");

        }
        LinkNode<E> s = new LinkNode<E>(e);
        LinkNode<E> p = geti(i-1);
        s.next = p.next;
        p.next = s;
    }

    public void Delete(int i){
        if (i<0||i>size()-1){
            throw new IllegalArgumentException("删除:位置i不在有效范围内");

        }
        LinkNode<E> p = geti(i-1);
        p.next=p.next.next;
    }

    @Override
    public String toString() {
        String ans = "";
        LinkNode<E> p = head.next;
        while (p!=null){
            ans+=p.data+"";
            p=p.next;
        }
        return ans;
    }
}

 这块代码有一些单链表的操作包括了头插尾插的应用。

package Date;

public class LinkListExam {
    public static void main(String[] args) {
        //测试一:数据为整型
        System.out.println("********测试1********");
        Integer[] a = {1, 2, 3, 4, 5};
        LinkListClass<Integer> L1 = new LinkListClass<>();

        //1.尾插法创建链表
        L1.CreateListR(a);
        System.out.println("L1:" + L1);
        System.out.println("L1长度=" + L1.size());

        //2.在末尾添加元素
        L1.Add(10);
        System.out.println("L1:" + L1);

        //遍历每个元素
        System.out.println("求每个序号的元素值");
        for (int i = 0; i < L1.size(); i++) {
            System.out.println("序号" + i + "的元素值" + L1.GetElem(i));

        }

        //重置长度
        System.out.println("重置长度为5");
        L1.Setsize(5);
        System.out.println("L1:" + L1);

        //在指定位置插入元素
        int i = 1;
        Integer x = 20;
        System.out.println("在序号"+i+"位置插入"+x);
        L1.Insert(i,x);
        System.out.println("L1:"+L1);


        //删除指定位置的元素
        i = 3;
        System.out.println("删除序号" + i + "的元素");
        L1.Delete(i);
        System.out.println("L1:" + L1);

        //替换指定位置的元素的值
        i = 2;
        x = 16;
        System.out.println("设置序号" + i + "的元素值为" + x);
        L1.SetElem(i, x);
        System.out.println("L1:" + L1);

        //获取序号为5的元素的序号
        x = 5;
        System.out.println("值为" + x + "的元素序号为" + L1.GetNo(x));

        //测试2:数据为字符
        System.out.println();
        System.out.println("********测试2********");
        Character[] b = {'a', 'b', 'c', 'd', 'e', 'f'};
        LinkListClass<Character> L2 = new LinkListClass<>();

        /*String str = "abcdef";
        str.codePoints().mapToObj(ch->(Character)ch).toArray();
        L2.CreateListR(str);
*/
        L2.CreateListR(b);
        System.out.println("L2:" + L2);
        System.out.println("L2长度=" + L2.size());

        L2.Add('x');
        System.out.println("L2:" + L2);

        System.out.println("求每个序号的元素值");
        for (i = 0; i < L2.size(); i++) {
            System.out.println("序号:"+i+"的元素值"+L2.GetElem(i));

        }
        //重置长度
        System.out.println("重置长度为5");
        L2.Setsize(5);
        System.out.println("L2:" + L2);

        //在指定位置插入元素
        int j = 1;
        Character l = 'k';
        System.out.println("在序号"+j+"位置插入"+l);
        L2.Insert(j,l);
        System.out.println("L2:"+L2);


        //删除指定位置的元素
        j = 3;
        System.out.println("删除序号" + j + "的元素");
        L2.Delete(j);
        System.out.println("L2:" + L2);

        //替换指定位置的元素的值
        j = 2;
        l = 't';
        System.out.println("设置序号" + j + "的元素值为" + l);
        L2.SetElem(j, l);
        System.out.println("L2:" + L2);

        //获取序号为5的元素的序号
        l = 'd';
        System.out.println("值为" + l + "的元素序号为" + L2.GetNo(l));



    }
}

     双链表        

package Date;


class DLinkNode<E> {
    E data;
    DLinkNode<E> prior;
    DLinkNode<E> next;
    public DLinkNode() {
        prior = null;
        next = null;
    }
    public DLinkNode(E d) {
        data = d;
        prior = null;
        next = null;
    }


}

   这段代码中有俩个方法,注释掉的是另外一种写法

package Date;



public class DLinkListClass<E> extends DLinkNode<E> {
    public DLinkNode<E> dhead;
    private int size;


    public DLinkListClass() {
        dhead = new DLinkNode<E>();
        dhead.prior = null;
        dhead.next = null;
    }


    public void CreateListF(E[] a) {
        DLinkNode<E> s;
        for (int i = 0; i < a.length; i++) {
            s = new DLinkNode<E>(a[i]);
            s.next = dhead.next;
            if (dhead.next != null) {
                dhead.next.prior = s;
                dhead.next = s;
                s.prior = dhead;
            }
        }
    }


//    public void CreateListR(E[] a) {
//        DLinkNode<E> s, t;
//        t = dhead;
//        for (int i = 0; i < a.length; i++) {
//            s = new DLinkNode<E>(a[i]);
//            t.next = s;
//            s.prior = t;
//            t = s;
//        }
//        t.next = null;
//
//    }
//
//
//    public void Insert(int i, E e) {
//        if (i < 0 || i > size())
//            throw new IllegalArgumentException("插入:位置i不在有效范围内。");
//        DLinkNode<E> s = new DLinkNode<E>(e);
//        DLinkNode<E> p = new geti(i - 1);
//        s.next = p.next;
//        if (p.next != null)
//            p.next.prior = s;
//        p.next = s;
//        s.prior = p;
//    }
//
//    public void Delete(int i) {
        if (i < 0 || i > size() - 1)
            throw new IllegalArgumentException("删除:位置i不再有效范围内");
        DLinkNode<E> p = geti(i);
        if(p.next!=null)
            p.next.prior=p.prior;
        p.prior.next=p.next;
    }
//
//
//
//    public DLinkListClass<E> geti(int i) {
//        DLinkNode<E> p = dhead;
//        int j = -1;
//        while (j < i) {
//            j++;
//            p = p.next;
//            p.prior=p;
//        }
//        return (DLinkListClass<E>) p;
//
//    }
//
//
//    private class geti extends DLinkNode<E> {
//        public geti(int i) {
//        }
//    }
}

  循环链表   

循环链表分为循环单链表和循环双链表 

    循环单链表泛型类

package Date;

public class CLinkListClass<E>{
    LinkNode<E>head;
    public CLinkListClass(){
        head=new LinkNode<E>();
        head.next=head;
    }

}

         循环双链表泛型    

package Date;

public class CDLinkListClass <E>{
   DLinkNode<E>dhead;
    public CDLinkListClass(){
        dhead=new DLinkNode<E>();
        dhead.prior=dhead;
        dhead.next=dhead;
    }
}

链表容器——Linkedlist

在Java中list接口还有另外一个重要的实现类——Linkedlist类,它采用循环双链表存储对象序列,可以看成链式存储结构的表在Java语言中的实现。

相关代码如下:

package Date;

import java.util.ArrayList;
import java.util.LinkedList;

public class Chapter2_3_7 {
    public static void main(String[]args){
        ArrayList <String>myarrlist=new ArrayList<String>();
        myarrlist.add("A");
        myarrlist.add("B");
        myarrlist.add("C");
        myarrlist.add("D");
        System.out.println("ArrayList:"+myarrlist);

        System.out.println("ArrayList->LinkedList");
        LinkedList<String>mylinklist=new LinkedList<String>(myarrlist);
        System.out.println("LinkedList:"+mylinklist);

        System.out.println("清空LinkedList并添加1,2,3");
        mylinklist.clear();
        mylinklist.add("1");
        mylinklist.add("2");
        mylinklist.add("3");
        System.out.println("LinkedList:"+mylinklist);

        myarrlist=new ArrayList<String>(mylinklist);
        System.out.println("LinkedList->ArrayList");
        System.out.println("ArrayList:"+myarrlist);

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值