【期末不挂科 数据结构】第二章 线性表

目录

2.1 线性表的定义及其逻辑结构

2.2 线性表的顺序存储和实现

2.3 线性表的链式存储和实现

2.4 顺序表和链表的比较

2.5 例题

2.6 写在最后


首先说明一下 期末不挂科 系列专为零基础计算机小白所写,因而不会涉及太多较难问题,基本上都是书本中的知识点和一些简单例题,建议搭配系列习题(会有详解~)作为巩固练习,预祝大家期末考试都不挂科,取得一个好成绩~

本章我们一起来学习一下线性表吧!(本章节的学习内容会有点多,但其实认真看的话还是很简单的,要坚持学下去哦,加油~)

线性表是一种最常用、最简单,也是最基本的数据结构,他是学习其他数据结构的基础。线性表在计算机中可以用顺序存储链式存储两种存储结构来表示。其中用顺序存储结构表示的线性表称为顺序表,用链式存储结构表示的线性表称为链表。链表又有单链表、双向链表、循环链表之分。

2.1 线性表的定义及其逻辑结构

线性表的定义:线性表是由n(n>=0)个类型相同的数据元素组成的有限序列,记作:

来看一张图理解一下吧: 

 小知识点来喽~

①元素个数n表的长度n=0时为空表

②ai是表中第i个元素,i是其位序a0为第0个元素

③ai-1和ai(0<=i<n)之间存在序偶关系<ai-1,ai>

        ai的直接前驱是ai-1,ai-1的直接后继是ai

        a0无直接前驱称为表头元素,an-1无直接后继称为表尾元素

接下来我们来学习一下线性表的基本操作吧:

// 线性表的抽象数据结构Java接口描述如下:
public interface Ilist{
    public void clear();    // clear() 将一个已经存在的线性表置成空表
    public boolean isEmpty();  // isEmpty() 判断线性表是否为空,若为空则返回true;否则返回false
    public int length();    // length() 求线性表中数据元素个数并返回其值
    public Object get(int i); // get(i) 读取并返回线性表中第i个元素的值。其中i的取值范围为0<=i<=length()-1
    public void insert(int i,Object x); // insert(i,x) 在线性表的第i个数据元素之前插入一个值为x的数据元素。其中取值范围为0<=i<=length()。当i=length()时,在表尾插入x
    public void remove(int i); // remove(i) 删除并返回线性表中第i个元素。其中i的取值范围为0<=i<=length()-1
    public int indexOf(Object x); // indexOf(x) 返回线性表中首次出现指定数据元素的位序号,若线性表中不包含此数据元素,则返回-1
    public void display(); // display() 输出线性表中的各个数据元素的值
}

2.2 线性表的顺序存储和实现

线性表的顺序存储结构称为顺序表。数据元素按照其逻辑次序依次存放到一片连续的地址空间,就形成了顺序表

顺序表存储示意图如下:

(a0,a1...an-1依序排至0,1...n-1位置上,原来在逻辑上相邻的数据元素在数组中也是相邻的)

 由此可知,数据元素在内存的物理存储次序反映了它们之间的逻辑关系。(物理上相邻那么他们逻辑上也相邻

顺序表是随机存储结构

小说明:

(1 ) SeqList<T>为泛型类,T表示顺序表数据元素的数据类型,创建该类实例时,T的实际参数为一个确定的类。
例如:String values[ ]={"A””B"”,"C”,"D”,”E”};
         SeqList<String> lista=new SeqList<String>(values);

 (2) Java的类是引用数据类型,一个对象引用一个实例。因此两个对象间的赋值是引用赋值,传递的值是对象引用,使得两个对象引用同一个实例。

我们先来学习一下顺序表的三种构造方法吧:

// 方法一
public SeqList(int length) // 构造容量为length的空表
{
    this.element = new Object[length]; // 申请数组的存储空间,元素为null
    this.n = 0;
}

// 方法二
public SeqList() // 创建默认容量的空表
{
    this(64);
/调用本类已声明的指定参数列表的构造方法
}

// 方法三
public SeqList(T[] values) // 构造顺序表,由values数组提供元素,忽略其中空对象
{
    this(values.length);  // 创建容量为values.length的空表
    for (int i=0; i<values.length; i++) // 复制数组元素,O(n)
        this.element[i] = values[i]; // 对象引用赋值
    this.n = element.length;
}

接下来我们来看一下插入、删除、查找操作算法:

①顺序表的插入操作算法

public void insert(int i,Object x) throws Exception{
    if(curLen == listElem.length)      // 判断顺序表是否已满
        throw new Exception("顺序表已满");   // 抛出异常
    if(i<0 || i > curLen)  // i 不合法
        throw new Exception("插入位置不合法"); // 抛出异常
    for(int j = curLen; j > i ; j--)
        listElem[j] = listElem[j - 1]; // 插入位置及其之后的所有数据元素后移一位
    listElem[i] = x; // 插入x
    curLen++;  // 表长加1
} // 算法结束

②顺序表的删除操作算法

public void remove(int i) throws Exception{
    if(i<0 || i > curLen - 1)  // i 不合法
        throw new Exception("删除位置不合法"); // 抛出异常
    for(int j = i; j < curLen - 1; j++)
        listElem[j] = listElem[j + 1]; // 被删除元素之后的所有数据元素后移一位
    curLen--; // 表长减1
} // 算法结束

③顺序表的查找操作算法

public int indexOf(Object x){
    int j = 0; // j 指示顺序表中待比较的数据元素,其初始值指示顺序表中第0个数据元素
    while(j < curLen &&!listElem[j].equals(x)) // 依次比较
        j++;
    if(j < curLen) // 判断 j 的位置是否位于顺序表中
        return j; // 返回值为 x 的数据元素在顺序表中的位置
    else
        return - 1; // 值为 x 的数据元素在顺序表中不存在
} // 算法结束

顺序线性表及其基本操作 

package ch02;

/**
 * 
 * 顺序线性表及其基本操作
 * 
 */
public class SqList implements IList {
	private Object[] listElem; // 线性表存储空间

	private int curLen; // 当前长度

	// 顺序表的构造函数,构造一个存储空间容量为maxSize的线性表
	public SqList(int maxSize) {
		curLen = 0; // 置顺序表的当前长度为0
		listElem = new Object[maxSize];// 为顺序表分配maxSize个存储单元
	}

	// 将一个已经存在的线性表置成空表
	public void clear() {
		curLen = 0; // 置顺序表的当前长度为0

	}

	// 判断当前线性表中数据元素个数是否为0,若为0则函数返回true,否则返回false
	public boolean isEmpty() {
		return curLen == 0;
	}
	
	
	// 求线性表中的数据元素个数并由函数返回其值
	public int length() {
		return curLen; // 返回顺序表的当前长度
	}

	// 读取到线性表中的第i个数据元素并由函数返回其值。其中i取值范围为:0≤i≤length()-1,如果i值不在此范围则抛出异常
	public Object get(int i) throws Exception {
		if (i < 0 || i > curLen - 1) // i小于0或者大于表长减1
			throw new Exception("第" + i + "个元素不存在"); // 输出异常

		return listElem[i]; // 返回顺序表中第i个数据元素
	}

	// 在线性表的第i个数据元素之前插入一个值为x的数据元素。其中i取值范围为:0≤i≤length()。如果i值不在此范围则抛出异常,当i=0时表示在表头插入一个数据元素x,当i=length()时表示在表尾插入一个数据元素x
	public void insert(int i, Object x) throws Exception {
		if (curLen == listElem.length) // 判断顺序表是否已满
			throw new Exception("顺序表已满");// 输出异常

		if (i < 0 || i > curLen) // i小于0或者大于表长
			throw new Exception("插入位置不合理");// 输出异常

		for (int j = curLen; j > i; j--)
			listElem[j] = listElem[j - 1];// 插入位置及之后的元素后移

		listElem[i] = x; // 插入x
		curLen++;// 表长度增1
	}

	// 将线性表中第i个数据元素删除。其中i取值范围为:0≤i≤length()- 1,如果i值不在此范围则抛出异常
	public void remove(int i) throws Exception {
		if (i < 0 || i > curLen - 1) // i小于1或者大于表长减1
			throw new Exception("删除位置不合理");// 输出异常

		for (int j = i; j < curLen - 1; j++)
			listElem[j] = listElem[j + 1];// 被删除元素之后的元素左移

		curLen--; // 表长度减1
	}

	// 返回线性表中首次出现指定元素的索引,如果列表不包含此元素,则返回 -1
	public int indexOf(Object x) {
		int j = 0;// j为计数器
		while (j < curLen && !listElem[j].equals(x))
			// 从顺序表中的首结点开始查找,直到listElem[j]指向元素x或到达顺序表的表尾
			j++;
		if (j < curLen)// 判断j的位置是否位于表中
			return j; // 返回x元素在顺序表中的位置
		else
			return -1;// x元素不在顺序表中
	}

	// 输出线性表中的数据元素
	public void display() {
		for (int j = 0; j < curLen; j++)
			System.out.print(listElem[j] + " ");
		System.out.println();// 换行

	}

	// 实现对顺序表就地逆置
	public void reverse() {
		for (int i = 0,j=curLen-1; i < j; i++,j--) {
			Object temp = listElem[i];
			listElem[i] = listElem[j];
			listElem[j] = temp;
		}
	}

	// 实现对顺序表右移k位
	public void shit(int k) {
		int n=curLen,p=0,i,j,l;
        Object temp;
     	for(i=1;i<=k;i++)
          if(n%i==0&&k%i==0) //求n和k的最大公约数p
              p=i;
        for(i=0;i<p;i++){
           j=i;
           l=(i+n-k)%n;
           temp=listElem[i];
           while(l!=i){
             listElem[j]=listElem[l];
             j=l;
             l=(j+n-k)%n;
           }// 循环右移一步
        listElem[j]=temp;
        }
    }

}

2.3 线性表的链式存储和实现

线性表的链式存储结构就是用任意的存储单元存储数据元素,这些存储单元可以连续也可以不连续,甚至可以零散分布在内存的任意位置

逻辑上相邻的数据元素在物理位置上不一定相邻,因此,必须采用附加信息表示数据元素之间的逻辑关系。如图:Node(结点) 

 数据域用来存储数据元素,地址域用来存储它的后继所在的地址信息

①单链表

接下来我们来看一下单链表(每个结点只有一个地址域)的示意图

 next域将数据元素按逻辑次序链接在一起, ^(空)最后一个元素an-1无后继所以为^, a0无前驱所以为head头指针

头结点:在单链表最前面增加一个特殊的结点,称为头结点,如下图所示。

 

 头结点无意义只是表示从此开始

接下来我们来看一下单链表结点类定义

// 单链表结点类定义
public class Node<T> // 单链表结点类,T指定结点的元素类型
{
    public T data;     // 数据域,存储数据元素
    public Node<T> next;     // 地址域,引用后继结点
    public Node(T data,Node<T> next)     // 构造结点,data指定数据元素,next指定后继结点{
        this.data = data;     // T 对象引用赋值
        this.next = next;    //Node<T>对象引用赋值
}
    public Node(){
        this(null, null);}
    public String toString()    //返回结点数据域的描述字符串
{
        return this.data.toString();
}

是不是看起来晕晕乎乎的?没关系(因为考试上面的东西不会单独考滴),所以我们来学习一下整体的吧,来看一下带头结点的单链表及其基本操作 :

package ch02;

/**
 * 
 * 带头结点的单链表及其基本操作
 * 
 */
import java.util.Scanner;

public class LinkList implements IList {
	public Node head;// 单链表的头指针

	// 单链表的构造函数
	public LinkList() {
		head = new Node(); // 初始化头结点
	}

	public LinkList(int n, boolean Order) throws Exception {
		this();// 初始化头结点
		if (Order) // 用尾插法顺序建立单链表
			create1(n);
		else
			// 用头插法逆位序建立单链表
			create2(n);
	}

	// 用尾插法顺序建立单链表。其中n为该单链表的元素个数
	public void create1(int n) throws Exception {
		Scanner sc = new Scanner(System.in);// 构造用于输入的对象
		for (int j = 0; j < n; j++)
			// 输入n个元素的值
			insert(length(), sc.next());// 生成新结点,插入到表尾
	}

	// 用头插法逆位序建立单链表。其中n为该单链表的元素个数
	public void create2(int n) throws Exception {
		Scanner sc = new Scanner(System.in);// 构造用于输入的对象
		for (int j = 0; j < n; j++)
			// 输入n个元素的值
			insert(0, sc.next());// 生成新结点,插入到表头
	}

	// 将一个已经存在的带头结点单链表置成空表
	public void clear() {
		head.data=null;
		head.next=null;
	}

	// 判断当前带头结点的单链表是否为空
	public boolean isEmpty() {
		return head.next == null;// 判断首结点是否为空
	}

	// 求带头结点单链表中的数据元素个数并由函数返回其值
	public int length() {
		Node p = head.next;// 初始化,p指向首结点,length为计数器
		int length = 0;
		while (p != null) {// 从首结点向后查找,直到p为空
			p = p.next;// 指向后继结点
			++length;// 长度增1
		}
		return length;
	}

	// 读取带头结点单链表中的第i个数据元素
	public Object get(int i) throws Exception {
		Node p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (p != null && j < i) {// 从首结点向后查找,直到p指向第i个元素或p为空
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}
		if (j > i || p == null) { // i小于0或者大于表长减1
			throw new Exception("第" + i + "个元素不存在");// 输出异常
		}
		return p.data;// 返回元素p
	}

	// 在带头结点单链表中第i个数据元素之前插入一个值为x的数据元素
	public void insert(int i, Object x) throws Exception {
		Node p = head;// 初始化p为头结点,j为计数器
		int j = -1; // 第i个结点前驱的位置
		while (p != null && j < i - 1) {// 寻找i个结点的前驱
			p = p.next;
			++j;// 计数器的值增1
		}
		if (j > i - 1 || p == null) // i不合法
			throw new Exception("插入位置不合理");// 输出异常

		Node s = new Node(x); // 生成新结点
		s.next=p.next;// 插入单链表中
		p.next=s;
	}

	// 将线性表中第i个数据元素删除。其中i取值范围为:0≤i≤length()- 1,如果i值不在此范围则抛出异常
	public void remove(int i) throws Exception {
		Node p = head;// p指向要删除结点的前驱结点
		int j = -1;
		while (p.next != null && j < i - 1) {// 寻找i个结点的前驱
			p = p.next;
			++j;// 计数器的值增1
		}
		if (j > i - 1 || p.next == null) { // i小于0或者大于表长减1
			throw new Exception("删除位置不合理");// 输出异常
		}
		p.next=p.next.next;// 删除结点
	}

	// 在带头结点的单链表中查找值为x的元素,如果找到,则函数返回该元素在线性表中的位置,否则返回-1
	public int indexOf(Object x) {
		Node p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (p != null && !p.data.equals(x)) {// 从单链表中的首结点元素开始查找,直到p.data指向元素x或到达单链表的表尾
			p = p.next;// 指向下一个元素
			++j;// 计数器的值增1
		}
		if (p != null)// 如果p指向表中的某一元素
			return j;// 返回x元素在顺序表中的位置
		else
			return -1;// x元素不在顺序表中
	}


	// 输出线性表中的数据元素
	public void display() {
		Node node = head.next;// 取出带头结点的单链表中的首结点元素
		while (node != null) {
			System.out.print(node.data + " ");// 输出数据元素的值
			node = node.next;// 取下一个结点
		}
		System.out.println();
	}

	// 在非递减的有序单链表中插入一个值为x的数据元素,并使单链表仍保持有序的操作
	//方法一
    public void insert(int  x) {
		Node p = head.next;
		Node q = head;// q用来记录p的前驱结点
		int temp;
		while (p != null) {
			temp = ((Integer) p.data).intValue();
			if (temp < x) {
				q = p;
				p = p.next;
			} else
				break;
		}

		Node s = new Node(x); // 生成新结点
		s.next=p;// 将s结点插入到单链表的q结点与p结点之间
		q.next=s;
	}
    // 在非递减的有序单链表中插入一个值为x的数据元素,并使单链表仍保持有序的操作
	//方法二
    public void insert1(int  x) {
		Node p = head.next;

		while (p.next != null&&((Integer) p.next.data).intValue()<x) {
            p = p.next;
		}
		Node s = new Node(x); // 生成新结点
		s.next=p.next;// 将s结点插入到单链表的q结点与p结点之间
		p.next=s;
	}

    // 实现对单链表就地逆置(采用的是头插法)
	public void reverse() {
		Node p = head.next;
		head.next=null;
		Node q;
		while (p != null) {
			q = p.next;
			p.next=head.next;
			head.next=p;
			p = q;
		}
	}

	// 实现删除单链表中数据域值等于x的所有结点的操作,并返回被删除结点的个数
	public int removeAll(Object x) {
		Node p = head.next;// 初始化,p指向首结点,j为计数器
		Node q = head; // 用来记录p的前驱结点
		int j = 0;// 用来记录被删除结点的个数
		while (p != null) { // 从单链表中的首结点开始对整个链表遍历一次
			if ((p.data).equals(x)) {
				q.next=p.next;
				++j;// 计数器的值增1
			} else
				q = p;
			p = p.next;// 指向下一个元素
		}
		return j;// 返回被删除结点的个数
	}
}

接下来是不带头结点的单链表及其基本操作

package ch02;


/**
 * 
 * 不带头结点的单链表及其基本操作
 * 
 */
public class LinkList2 implements IList {

	public Node head;// 单链表的首结点指针

	// 构造函数
	public LinkList2() {
		head = null;
	}

	// 将一个已经存在的单链表置成空表
	public void clear() {
		head = null;
	}

	// 判断当前单链表是否为空
	public boolean isEmpty() {
		return head == null;
	}

	// 求单链表中的数据元素个数并由函数返回其值
	public int length() {
		Node p = head;// 初始化,p指向首结点,length为计数器
		int length = 0;
		while (p != null) {// 从首结点向后查找,直到p为空
			p = p.next;// 指向后继结点
			++length;// 长度增1
		}
		return length;
	}

	// 读取单链表中的第i个数据元素
	public Object get(int i) throws Exception {
		Node p = head;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (p != null && j < i) {// 从首结点向后查找,直到p指向第i个元素或p为空
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}
		if (j > i || p == null) // i小于0或者大于表长减1
			throw new Exception("第" + i + "个元素不存在");// 输出异常

		return p.data;// 返回元素p
	}

	// 在单链表中第i个数据元素之前插入一个值为x的数据元素
	public void insert(int i, Object x) throws Exception {
		Node s = new Node(x);
		if (i == 0) { // 插入位置为表头
			s.next=head;
			head = s;
			return;
		}

		Node p = head;
		int j = 0;// 第i个结点前驱的位置
		while (p != null && j < i - 1) {// 寻找i个结点的前驱
			p = p.next;
			++j;
		}
		if (j > i - 1 || p == null)
			throw new Exception("插入位置不合理");

		// 插入位置为表的中间或表尾
		s.next=p.next;
		p.next=s;
	}

	// 将线性表中第i个数据元素删除。其中i取值范围为:0≤i≤length()- 1,如果i值不在此范围则抛出异常
	public void remove(int i) throws Exception {
		Node p = head;// 初始化p为首结点,j为计数器
		Node q = null; // 用来记录p的前驱结点
		int j = 0;
		while (p != null && j < i) {// 寻找i个结点
			q = p;
			p = p.next;
			++j;// 计数器的值增1
		}
		if (j > i || p == null) // i小于0或者大于表长减1
			throw new Exception("删除位置不合理");// 输出异常

		if (q == null)
			head = null;// 删除首结点
		else
			q.next=p.next;// 删除其他结点
	}

	// 实现删除单链表中数据域值等于x的第一个结点的操作。若删除成功,则返回被删除结点的位置;否则,返回-1。
	public int remove(Object x) {
		Node p = head;// 初始化,p指向首结点
		Node q=null;  //q用来记录p的前驱结点
        int j = 0; //j为计数器
		while (p != null && !p.data.equals(x)) {// 从单链表中的首结点元素开始查找,直到p.data指向元素x或到达单链表的表尾
			 q=p;
             p = p.next;// 指向下一个元素
			 ++j;// 计数器的值增1
		 }
		if (p!=null&&q==null) //删除的是单链表中的首结点
             head=p.next;
        else if (p != null) {// 删除的是单链表中的非首结点
			   q.next=p.next;
		     }
        else
			 return -1;//值为x的结点在单链表中不存在
	    return j;
    }


	// 在不带头结点的单链表中查找值为x的元素,如果找到,则函数返回该元素在线性表中的位置,否则返回-1
	public int indexOf(Object x) {
		Node p = head;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (p != null && !p.data.equals(x)) {// 从单链表中的首结点元素开始查找,直到p.data指向元素x或到达单链表的表尾
			p = p.next;// 指向下一个元素
			++j;// 计数器的值增1
		}
		if (p != null)// 如果p指向表中的某一元素
			return j;// 返回x元素在顺序表中的位置
		else
			return -1;// x元素不在顺序表中
	}

	// 输出线性表中的数据元素
	public void display() {
		Node node = head;// 取出带头结点的单链表中的首结点元素
		while (node != null) {
			System.out.print(node.data + " ");// 输出数据元素的值
			node = node.next;// 取下一个结点
		}
		System.out.println();// 换行
	}

}

基本上把这俩看会应付考试就差不多啦~

②双链表

双链表是每个结点有两个地址域的线性链表,两个地址域分别指向前驱结点和后继结点。

 双链表的两个结构如下:

 在双链表中,知道某个结点的引用找前驱和后继都是非常容易,因为直接就有两个地址域来指向前驱和后继。

①双链表的插入操作

1:  q.prior=p.prior; 

2:p.prior.next=q;

3:  p.prior=q; 

4:  q.next=p;

注意1234的顺序不可以错, 双链表如果要插入一个元素,正向和逆向的都要插入才可以(链接结点也要更换)

②双链表的删除操作

 p.prior.next=p.next; 

p.next.prior=p.prior;

③循环单链表
将最后一个结点的next域指向head,形成环形结构,这就是循环单链表。

循环单链表与单链表操作算法基本相同只是判断条件不同:

单链表p指向尾结点时: p.next=null

循环单链表p指向尾结点时:p.next=head

循环单链表设置尾指针找开始结点和终端结点都很方便
开始结点:rear.next.next        终端结点:rear

因此,实际应用中多采用尾指针指示的循环单链表

④循环双链表

 双向循环链表及其基本操作

package ch02;

import java.util.Scanner;

/**
 * 
 * 双向循环链表及其基本操作
 * 
 */
public class DuLinkList implements IList {
	public DuLNode head;// 双向循环链表的头结点

	// 双向链表的构造函数
	public DuLinkList() {
		head = new DuLNode(); // 初始化头结点
		head.prior=head;// 初始化头结点的前驱和后继
		head.next=head;
	}

	// 从表尾到表头逆向建立双向链表的算法。其中n为该双向链表的元素个数
	public DuLinkList(int n) throws Exception {
		this();
		Scanner sc = new Scanner(System.in);// 构造用于输入的对象
		for (int j = 0; j < n; j++)
			insert(0, sc.next());// 生成新结点,插入到表头
	}

	// 在双向循环链表的第i个数据元素之前插入一个值为x的数据元素,i等于表长时,p指向头结点;i大于表长时,p=NULL。
	// 其中i取值范围为:0≤i≤length()。当i=0时表示在表头插入一个数据元素x,当i=length()时表示在表尾插入一个数据元素x
	public void insert(int i, Object x) throws Exception {
		DuLNode p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (!p.equals(head) && j < i) {// 寻找插入位置i
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}

		if (j != i && !p.equals(head)) // i小于0或者大于表长
			throw new Exception("插入位置不合理");// 输出异常

		DuLNode s = new DuLNode(x);// 生成新结点
		p.prior.next=s;
		s.prior=p.prior;
		s.next=p;
		p.prior=s;
	}

	// 将双向循环链表中第i个数据元素删除。其中i 取值范围为:0≤i≤ength()-1
	public void remove(int i) throws Exception {
		DuLNode p = head.next;// 初始化,p指向首节点结点,j为计数器
		int j = 0;
		while (!p.equals(head) && j < i) {// 寻找删除位置i
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}

		if (j != i) // i小于0或者大于表长减1
			throw new Exception("删除位置不合理");// 输出异常

		p.prior.next=p.next;
		p.next.prior=p.prior;
	}

	// 将一个已经存在的双向循环链表置成空表
	public void clear() {
		head.prior=head;
		head.next=head;
	}

	// 判断当前双向循环链表是否为空
	public boolean isEmpty() {
		return head.equals(head.next);
	}

	// 读取双向循环链表中的第i个数据元素
	public Object get(int i) throws Exception {
		DuLNode p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (!p.equals(head) && j < i) {// 从首结点向后查找,直到p指向第i个元素或p指向头结点
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}
		if (j > i || p.equals(head)) { // i小于0或者大于表长减1
			throw new Exception("第" + i + "个元素不存在");// 输出异常
		}
		return p.data;// 返回元素p
	}

	// 求双向循环链表中的数据元素个数并由函数返回其值
	public int length() {
		DuLNode p = head.next;// 初始化,p指向首结点,length为计数器
		int length = 0;
		while (!p.equals(head)) {// 从首结点向后查找,直到p指向头结点
			p = p.next;// 指向后继结点
			++length;// 长度增1
		}
		return length;
	}

	// 在双向循环链表中查找值为x的元素,如果找到,则函数返回该元素在线性表中的位置,否则返回-1
	public int indexOf(Object x) {
		DuLNode p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (!p.equals(head) && !p.data.equals(x)) {// 从链表中的首结点元素开始查找,直到p.data指向元素x或到达链表的表尾
			p = p.next;// 指向下一个元素
			++j;// 计数器的值增1
		}
		if (!p.equals(head))// 如果p指向表中的某一元素
			return j;// 返回x元素在顺序表中的位置
		else
			return -1;// x元素不在顺序表中
	}

	public void display() {
		DuLNode node = head.next;// 取出带头结点的双向链表中的首结点
		while (!node.equals(head)) {
			System.out.print(node.data + " ");// 输出数据元素的值
			node = node.next;
		}
		System.out.println();
	}

	public DuLNode getHead() {
		return head;
	}

	public void setHead(DuLNode head) {
		this.head = head;
	}

}

 带头结点的循环链表及其基本操作类

package ch02;


/**
 * 
 * 带头结点的循环链表及其基本操作类
 * 
 */
public class CircleLinkList implements IList {
	public Node head;// 循环单链表的头指针

	// 循环单链表的构造函数
	public CircleLinkList() {
		head = new Node(); // 初始化头结点
		head.next=head;
	}

	// 将一个已经存在的带头结点的循环单链表置成空表
	public void clear() {
		head.next=head;
	}

	// 判断当前带头结点的循环单链表是否为空
	public boolean isEmpty() {
		return head.next.equals(head);
	}

	// 求带头结点的循环单链表中的数据元素个数并由函数返回其值
	public int length() {
		Node p = head.next;// 初始化,p指向首结点,length为计数器
		int length = 0;
		while (!p.equals(head)) {// 从首结点向后查找,直到p指向头结点
			p = p.next;// 指向后继结点
			++length;// 长度增1
		}
		return length;
	}

	// 读取带头结点的循环单链表中的第i个数据元素
	public Object get(int i) throws Exception {
		Node p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (!p.equals(head) && j < i) {// 从首结点向后查找,直到p指向第i个元素或p指向头结点
			p = p.next;// 指向后继结点
			++j;// 计数器的值增1
		}
		if (j > i || p.equals(head)) { // i小于0或者大于表长减1
			throw new Exception("第" + i + "个元素不存在");// 输出异常
		}
		return p.data;// 返回元素p
	}

	// 在带头结点的循环单链表中第i个数据元素之前插入一个值为x的数据元素
	public void insert(int i, Object x) throws Exception {
		Node p = head;// 初始化p为头结点,j为计数器
		int j = -1; // 第i个结点前驱的位置
		while ((!p.equals(head) || j == -1) && j < i - 1) {// 寻找i个结点的前驱
			p = p.next;
			++j;// 计数器的值增1
		}
		if (j > i - 1 || (p.equals(head) && j != -1)) // i不合法
			throw new Exception("插入位置不合理");// 输出异常

		Node s = new Node(x); // 生成新结点
		s.next=p.next;// 插入单链表中
		p.next=s;
	}

	// 将循环单链表中第i个数据元素删除。其中i取值范围为:0≤i≤length()- 1,如果i值不在此范围则抛出异常
	public void remove(int i) throws Exception {
		Node p = head;// p指向要删除结点的前驱结点
		int j = -1;
		while ((!p.next.equals(head) || j == -1) && j < i - 1) {// 寻找i个结点的前驱
			p = p.next;
			++j;// 计数器的值增1
		}
		if (j > i - 1 || (p.next).equals(head)) { // i小于0或者大于表长减1
			throw new Exception("删除位置不合理");// 输出异常
		}
		p.next=p.next.next;// 删除结点
	}

	// 在带头结点的循环单链表中查找值为x的元素,如果找到,则函数返回该元素在线性表中的位置,否则返回-1
	public int indexOf(Object x) {
		Node p = head.next;// 初始化,p指向首结点,j为计数器
		int j = 0;
		while (!p.equals(head) && !p.data.equals(x)) {// 从单链表中的首结点元素开始查找,直到p.data指向元素x或到达单链表的表尾
			p = p.next;// 指向下一个元素
			++j;// 计数器的值增1
		}
		if (!p.equals(head))// 如果p指向表中的某一元素
			return j;// 返回x元素在顺序表中的位置
		else
			return -1;// x元素不在顺序表中
	}

	// 输出循环链表中的数据元素
	public void display() {
		Node node = head.next;// 取出带头结点的单链表中的首结点元素
		while (!node.equals(head)) {
			System.out.print(node.data + " ");// 输出数据元素的值
			node = node.next;// 取下一个结点
		}
		System.out.println();// 换行
	}


}

2.4 顺序表和链表的比较

①时间性能比较

随机访问操作:
顺序表
对于按位置随机访问的操作,时间性能为O(1)            
链表按位置访问只能从头开始依次向后扫描,所需的平均时间为O(n)

插入删除操作:
链表不需要移动数据元素,插入或删除后继结点所需的时间为O(1)
顺序表需要移动数据元素,时间性能为O(n)

(如果删除后继结点用链表方便些)

一般规律:

若线性表需要频繁进行查找却很少进行插入或删除操作,或者操作和数据元素的位序密切相关时,宜采用顺序表作为存储结构。
若线性表需要频繁进行插入和删除操作,则宜采用链表作为存储结构

②空间性能比较

结点存储密度:
顺序表
只需存储数据元素引用,存储空间利用率较高。
链表除了存储数据元素引用变量外,还要存储元素之间逻辑关系的引用变量,引用的结构性开销占了整个结点的一半。

存储空间分配:
顺序表
需要分配一定长度的存储空间,分配过大,存储空间得不到充分利用,造成浪费,分配过小,则会产生溢出;
链表不需要固定长度的存储空间,对链表中的元素个数没有限制。

一般规律:
当线性表中元素个数变化较大或未知时,最好采用链表实现。

如果事先知道线性表的大致长度,使用顺序表的空间利用率会更高。

2.5 例题

1.判断题

1-1 对于顺序存储的长度为N的线性表,删除第一个元素和插入最后一个元素的时间复杂度分别对应为O(1)和 O(N)。F

解析:因为是顺序存储,所以删除第一个元素后所有元素都需向前移动一位

1-2 在线性表的顺序存储结构中,插入和删除元素时,移动元素的个数与该元素的位置有关。  T

1-3 顺序存储的线性表可以随机存取。  T

1-4 顺序表 - 存储结构

顺序表中逻辑上相邻的元素,其物理位置也一定相邻。  T

1-5 线性表采用链式存储结构时,各个数据元素的存储单元地址一定是不连续的。 F

解析:连续与否均可 

1-6 线性表采用链表存储时,结点和节点内部的存储空间可以是不连续的。 F

 解析:内部的存储空间是连续的,节点之间可以是不连续的

1-7 对单链表来说,只有从头结点开始才能访问到表中所有结点。 (1分) T

2.单选题

2-5 在一个长度为n的顺序表中,删除第i个元素(1≤i≤n)时需要移动( )个元素。

A. n-i B. n-i+1 C. n-i-1 D. i  

2-6 在下列对顺序表进行的操作中,算法时间复杂度为O(1)的是( A  )

 A. 访问第i个元素的前驱 B.在第i个元素之后插入一个新元素(1<=i<=n ) C. 删除第i个元素(1<=i<=n ) D. 对顺序表中元素进行排序

2-7 将两个各有n个元素的递增有序顺序表归并成一个有序顺序表,其最少的比较次数是( A

 A. n B. 2n-1 C. 2n D. n-1

2-8 在单链表中,将 s 所指新结点插入到 p 所指结点之后,其语句应该为 ▁▁▁▁▁ 。

A. p.next = s; s.next = p.next; B. s.next= p.next; p.next = s.next; C. s.next = p.next; p.next = s; D. p.next = s.next; s.next = p.next;

2-9 对于一个头指针为p的带头结点的单循环链表,判定该表为空表的条件是 __C__ 。

A. p.next==NULL B. p==NULL C. p.next==p D. p!=NULL

(2-8/9不会的宝子返回看一下单链表和循环链表哦)

2-10 在双链表中,删除 s 所指代结点,其语句应该为 ▁▁▁A▁▁。

A. s.next.prior = s.prior; s.prior.next = s.next; B. s.next = s.next.next; s.next.prior = s; C. s.prior.next = s; s.prior = s.prior.prior; D. s.prior = s.next.next; s.next = s.prior.prior;

2-11 在双链表中,删除 p 所指代结点的后继结点,其语句应该为 ▁▁C▁▁▁ 。

A. s = p; s.next.prior = p; p.next = s.next; B. s = p; s.next.prior = p.prior; p.next = s.next; C. s = p.next; s.next.prior= p; p.next = s.next; D. s = p.next; s.next.prior = p; p.next = s.next.next

(2-10/11不会的宝子们可以返回再看一下双链表的知识,静下心来慢慢分析一下哦,可能这种题第一次做很慢,但是多做慢慢就可以快速理解啦,熟能生巧哦)

填空题 

4-1 数组A[1..5,1..6]每个元素占5个单元,将其按行优先次序存储在起始地址为1000的连续的内存单元中,则元素A[5,6]的地址为: 1145 

解析:1000+5*6*5-5=1145    起始地址为1000,[1,1] [1,2]……[5,6]一共为5*6个元素,每个元素占5单元,所以一共占5*6*5,再加上起始地址,就是总的,题干问[5,6]的地址,此时这个位置还未存储,所以减五,即1000+5*6*5-5=1145

4-2 在有n个元素的顺序表中的任意位置插入一个元素所需移动元素的平均次数为 n/2

解析:n个元素的顺序表可插入的位置有n+1个,移动次数总数为0+1+2…+n=(0+n)*(n+1)/2,平均次数为 总数/(n+1)= n/2

2.6 写在最后

①搭配课后习题巩固学习效果会更好哦~(课后习题更新中)

②该系列数据结构使用语言为Java,但是数据结构本身是一种逻辑上的概念,它是独立于特定语言的,所以学习数据结构不必拘泥于某种特定语言,每种语言都可以实现特定的数据结构,差别只在于语法实现级别。

③该系列数据结构知识点内容参照 中国大学mooc 徐爱琴老师的数据结构java(链接在第一章)d29101a997c842fdaead9894659bde4a.jpeg

④ 该系列数据结构代码案例与课后习题参照书本 《数据结构——Java语言描述》f45225cfc07d45e58245adc22cc08f4c.jpeg

该系列数据结构为梨子(我)在学习数据结构时总结出的知识点,例题为自己做题时觉得还不错的题目,习题为书本课后习题。如有任何错误希望大家可以多多指正哈~如果觉得我的文章写的还不错,可以给个三连!!!我也会按照自己的学习进度持续更新的哦~

哦对,这一章的学习可能对部分同学来说有些难度,看的云里雾里的,没关系哈,梨子刚学的时候也很懵,静下心来多看看多理解,如果实在不会的话就多做做习题,从做题中找感觉,慢慢理解了。

最后希望大家都能通过数据结构这门考试并且有所收获,我们一起加油吧!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
1. 对一个算法的评价,不包括如下( )方面的内容。 A.健壮性和可读性 B.并行性 C.正确性 D.时空复杂度 2. 在带有头结点单链表HL,要向表头插入一个由指针p指向的结点,则执行( )。 A. p->next=HL->next; HL->next=p; B. p->next=HL; HL=p; C. p->next=HL; p=HL; D. HL=p; p->next=HL; 3. 对线性表,在下列哪种情况下应当采用链表表示?( ) A.经常需要随机地存取元素 B.经常需要进行插入和删除操作 C.表元素需要占据一片连续的存储空间 D.表元素的个数不变 4. 一个栈的输入序列为1 2 3,则下列序列不可能是栈的输出序列的是( ) A. 2 3 1 B. 3 2 1 C. 3 1 2 D. 1 2 3 5. 采用开放定址法处理散列表的冲突时,其平均查找长度( )。 A.低于链接法处理冲突 B. 高于链接法处理冲突 C.与链接法处理冲突相同 D.高于二分查找 6. 若需要利用形参直接访问实参时,应将形参变量说明为( )参数。 A. B.函数 C.指针 D.引用 7. 在稀疏矩阵的带行指针向量的链接存储,每个单链表结点都具有相同的( )。 A.行号 B.列号 C.元素 D.非零元素个数 8. 快速排序在最坏情况下的时间复杂度为( )。 A.O(log2n) B.O(nlog2n) C.0(n) D.0(n2) 9. 从二叉搜索树查找一个元素时,其时间复杂度大致为( )。 A. O(n) B. O(1) C. O(log2n) D. O(n2) 10. 栈和队列的共同特点是( )。 A.只允许在端点处插入和删除元素 B.都是先进后出 C.都是先进先出 D.没有共同点 11. 用链接方式存储的队列,在进行插入运算时( ). A. 仅修改头指针   B. 头、尾指针都要修改 C. 仅修改尾指针 D.头、尾指针可能都要修改 12. 以下数据结构哪一个是非线性结构?( ) A. 队列    B. 栈 C. 线性表    D. 二叉树 13. 树最适合用来表示( )。 A.有序数据元素 B.无序数据元素 C.元素之间具有分支层次关系的数据 D.元素之间无联系的数据 14. 二叉树的第k层的结点数最多为( ). A.2k-1 B.2K+1 C.2K-1    D. 2k+1 15. 若有18个元素的有序表存放在一维数组A[19],第一个元素放A[1],现进行二分查找,则查找A[3]的比较序列的下标依次为( ) A. 1,2,3 B. 9,5,2,3 C. 9,5,3 D. 9,4,2,3 16. 对n个记录的文件进行快速排序,所需要的辅助存储空间大致为 A. O(1)   B. O(n)   C. O(1og2n) D. O(n2) 17. 对于线性表(7,34,55,25,64,46,20,10)进行散列存储时,若选用H(K)=K %9作为散列函数,则散列地址为1的元素有( )个, A.1 B.2 C.3 D.4 18. 设有6个结点的无向图,该图至少应有( )条边才能确保是一个连通图。 A.5 B.6 C.7 D.8 19.设数组data[m]作为循环队列SQ的存储空间,front为队头指针,rear为队尾指针,则执行出队操作后其头指针front值为( ) A.front=front+1 B.front=(front+1)%(m-1) C.front=(front-1)%m D.front=(front+1)%m 20.如下陈述正确的是( ) A.串是一种特殊的线性表 B.串的长度必须大于零 C.串元素只能是字母 D.空串就是空白串 21.设一组初始记录关键字序列为(345,253,674,924,627),则用基数排序需要进行( )趟的分配和回收才能使得初始关键字序列变成有序序列。 (A) 3 (B) 4 (C) 5 (D) 8 22.设用链表作为栈的存储结构则退栈操作( )。 (A) 必须判别栈是否为满 (B) 必须判别栈是否为空 (C) 判别栈元素的类型 (D) 对栈不作任何判别 23.下列四种排序( )的空间复杂度最大。 (A) 快速排序 (B) 冒泡排序 (C) 希尔排序 (D) 堆 24.数据的最小单位是( )。 (A) 数据项 (B) 数据类型 (C) 数据元素 (D) 数据变量 25.设一组初始记录关键字序列为(50,40,95,20,15,70,60,45),则以增量d=4的一趟希尔排序结束后前4条记录关键字为( )。 (A) 40,50,20,95 (B) 15,40,60,20 (C) 15,20,40,45 (D) 45,40,15,20 26.设一组初始记录关键字序列为(25,50,15,35,80,85,20,40,36,70),其含有5个长度为2的有序子表,则用归并排序的方法对该记录关键字序列进行一趟归并后的结果为( )。 (A) 15,25,35,50,20,40,80,85,36,70 (B) 15,25,35,50,80,20,85,40,70,36 (C) 15,25,35,50,80,85,20,36,40,70 (D) 15,25,35,50,80,20,36,40,70,85 27. 设一组权集合W={2,3,4,5,6},则由该权集合构造的哈夫曼树带权路径长度之和为( )。 (A) 20 (B) 30 (C) 40 (D) 45 28.下面程序的时间复杂为( )。 for(i=1,s=0; i<=n; i++) { t=1; for(j=1;j<=i;j++) t=t*j;s=s+t; } A.O(n) B.O(n2) C.O(n3) D.O(n4) 29.递归过程必定用到的数据结构是( )。 A.循环队列 B.堆栈 C.二叉树 D.散列 30.设按照从上到下、从左到右的顺序从1开始对完全二叉树进行顺序编号,则编号为i结点的左孩子结点的编号为( )。 (A) 2i+1 (B) 2i (C) i/2 (D) 2i-1 31.设一组初始记录关键字序列为(13,18,24,35,47,50,62,83,90,115,134),则利用二分法查找关键字90需要比较的关键字个数为( )。 (A) 1 (B) 2 (C) 3 (D) 4 32.设指针变量top指向当前链式栈的栈顶,则删除栈顶元素的操作序列为( )。 (A) top=top+1; (B) top=top-1; (C) top->next=top; (D) top=top->next; 33. 字符串的长度是指( )。 (A) 串不同字符的个数 (B) 串不同字母的个数 (C) 串所含字符的个数 (D) 串不同数字的个数 34. 队列是一种( )的线性表。 (A) 先进先出 (B) 先进后出 (C) 只能插入 (D) 只能删除 35.设散列表有m个存储单元,散列函数H(key)= key % p,则p最好选择( )。 (A) 小于等于m的最大奇数 (B) 小于等于m的最大素数 (C) 小于等于m的最大偶数 (D) 小于等于m的最大合数 36.如下代码段输出的结果是( )。 String s1 = "abc"; String s2 = new String(s1); System.out.println(s1 == s2 + ","); System.out.println(s1.equals(s2)); A.true,true B.false,false C.true,false D.false ,true 37.二叉树有( )种不同的基本形态。 A.3 B.4 C.5 D.6 38.设一组初始记录关键字序列为(Q,H,C,Y,P,A,M,S,R,D,F,X),则按字母升序的第一趟冒泡排序结束后的结果是( )。 (A) F,H,C,D,P,A,M,Q,R,S,Y,X (B) P,A,C,S,Q,D,F,X,R,H,M,Y (C) A,D,C,R,F,Q,M,S,Y,P,H,X (D) H,C,Q,P,A,M,S,R,D,F,X,Y 39.设顺序循环队列Q[0:M-1]的头指针和尾指针分别为F和R,头指针F总是指向队头元素的前一位置,尾指针R总是指向队尾元素的当前位置,则该循环队列的元素个数为(C )。 (A) R-F (B) F-R (C) (R-F+M)%M (D) (F-R+M)%M 40.设某棵二叉树的序遍历序列为ABCD,前序遍历序列为CABD,则后序遍历该二叉树得到序列为( )。 (A) BADC (B) BCDA (C) CDAB (D) CBDA 41.设某完全无向图有n个顶点,则该完全无向图有( )条边。 (A) n(n-1)/2 (B) n(n-1) (C) n2 (D) n2-1 42.设某棵二叉树有2000个结点,则该二叉树的最小高度为( )。 (A) 9 (B) 10 (C) 11 (D) 12 43.设一组初始关键字记录关键字为(20,15,14,18,21,36,40,10),则以20为基准记录的一趟快速排序结束后的结果为( A )。 (A) 10,15,14,18,20,36,40,21 (B) 10,15,14,18,20,40,36,21 (C) 10,15,14,20,18,40,36,2l (D) 15,10,14,18,20,36,40,21 44.设一组初始记录关键字序列为(345,253,674,924,627),则用基数排序需要进行()趟的分配和回收才能使得初始关键字序列变成有序序列。 (A) 3 (B) 4 (C) 5 (D) 8 45.设有n个关键字具有相同的Hash函数,则用线性探测法把这n个关键字映射到Hash表需要做( )次线性探测。 A.2 B.n(n+1) C.n(n+1)/2 D.n(n-1)/2 46.设一组初始记录关键字序列为(13,18,24,35,47,50,62,83,90,115,134),则利用二分法查找关键字90需要比较的关键字个数为( )。 A.1 B.2 C.3 D.4 47.设二叉树的先序遍历序列和后序遍历序列正好相反,则该二叉树满足的条件是( )。 A.空或只有一个结点 B.高度等于其结点数 C.任一结点无左孩子 D.任一结点无右孩子 48.设有n个待排序的记录关键字,则在堆排序需要()个辅助记录单元。 (A) 1 (B) n (C) nlog2n (D) n2 49.下面关于线性表的叙述错误的是( )。 (A) 线性表采用顺序存储必须占用一片连续的存储空间 (B) 线性表采用链式存储不必占用一片连续的存储空间 (C) 线性表采用链式存储便于插入和删除操作的实现 (D) 线性表采用顺序存储便于插入和删除操作的实现 50.将10阶对称矩阵压缩存储到一维数组A,则数组A的长度最少为( )。 (A) 100 (B) 40 (C) 55 (D) 80 51数据结构是指数据及其相互之间的______________。当结点之间存在M对N(M:N)的联系时,称这种结构为_____________________。 52.一组初始记录关键字序列为(Q,H,C,Y,P,A,M,S,R,D,F,X),则按字母升序的第一趟冒泡排序结束后的结果是 。 53.队列的插入操作是在队列的___尾______进行,删除操作是在队列的____首______进行。54.根据搜索方法的不同,图的遍历有 和 两种。 55.堆栈的操作特点是 。 56. 若对一棵完全二叉树从0开始进行结点的编号,并按此编号把它顺序存储到一维数组A,即编号为0的结点存储到A[0]。其余类推,则A[ i ]元素的左孩子元素为________,右孩子元素为_______________,双亲元素为____________。 57. 快速排序算法的平均时间复杂度为____________,直接插入排序算法的平均时间复杂度为___________。 58.对于一个长度为n的单链存储的线性表,在表头插入元素的时间复杂度为_________,在表尾插入元素的时间复杂度为____________。 59.一棵高度为5的二叉树最少含有______个结点,最多含有_____个结点。 11.对于给定的若干个元素,可以构造出的逻辑结构有 结构, 结构和 结构三种。 60.后缀算式9 2 3 +- 10 2 / -的值为__________。缀算式(3+4X)-2Y/3对应的后缀算式为_______________________________。 61.设完全二叉树的顺序存储结构存储数据ABCDE,要求给出该二叉树的链式存储结构并给出该二叉树的前序、序和后序遍历序列。 62.设给定一个权集合W=(3,5,7,9,11),要求根据给定的权集合构造一棵哈夫曼树并计算哈夫曼树的带权路径长度WPL。 63.设一组初始记录关键字序列为(19,21,16,5,18,23),要求给出以19为基准的一趟快速排序结果以及第2趟直接选择排序后的结果。 64..线性表的存储结构有哪两种,各有何特点? 65.在顺序循环队列,什么是假溢出?请指出解决假溢出的常见方法。 66.阅读下面的算法: LinkList mynote(LinkList L) {//L是不带头结点单链表的头指针 if(L&&L->next){ q=L;L=L->next;p=L; S1: while(p->next) p=p->next; S2: p->next=q;q->next=NULL; } return L; } 语句S1和语句组S2的功能是什么? 67.已知一个无向图的顶点集为{a, b, c, d, e} ,其邻接矩阵如下所示 (1)画出该图的图形; (2)根据邻接矩阵从顶点a出发进行深度优先遍历和广度优先遍历,写出相应的遍历序列。 68.已知关键字序列为{8,17,26,32,40,72,87,99},采用折半查找算法,给定值为70,35时分别与哪些元素比较?画出相应的二叉判定树 69. 设散列表的长度为8,散列函数H(k)=k mod 7,初始记录关键字序列为(25,31,8,27,13,68),要求分别计算出用线性探测法和链地址法作为解决冲突方法的平均查找长度。 70.对于KMP算法,模式串每个字符的最大真子串构成一个数组,定义为模式串的next[j]函数。next[j]函数定义如下: Max{k|0<k<j且“t0t1…tk-1”=“tj-ktj-k+1…tj-1”} 当此集合非空时 next[j]= 0 其他情况 -1 当j=0 计算t=“abcabaa”的next[j],要求写出具体的推算步骤。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梨子想当程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值