[转]线性表的链式存储
(java版之单向线性)
第一步,定义一个接口
- package com.stucture.list;
- /**
- * 线性表顺序存储结构的接口
- * 指的是用一段地址连续的存储单元一次存储线性表的数据元素
- * @ClassName: ISqList
- * @author 小学徒
- * @date 2013-2-27
- */
- public interface IList<T> {
- /**
- * 获得元素
- * @param loc 需要获得的第loc个元素
- * @return
- */
- public T getElem(int loc);
- /**
- * 插入元素
- * @param loc 元素的插入位置
- * @param t 需要插入的元素
- * @return 是否成功插入
- */
- public boolean insertElem(int loc, T t);
- /**
- * 删除元素
- * @param i 需要删除元素的位置
- * @return
- */
- public T deleteElem(int i);
- }
第二步,定义我们的节点类:
- package com.stucture.list.linkList;
- /**
- * 链表中的结点
- * @ClassName: Node
- * @author 小学徒
- * @date 2013-2-27
- */
- public class Node<T> {
- private T data; //需要存储的数据信息
- private Node<T> next; //后继<span style="font-size: 1em; line-height: 1.5;">结</span><span style="font-size: 1em; line-height: 1.5;">点 </span>
- public T getData() {
- return data;
- }
- public void setData(T data) {
- this.data = data;
- }
- public Node<T> getNext() {
- return next;
- }
- public void setNext(Node<T> next) {
- this.next = next;
- }
- }
第三步,定义我们的链表及其基本操作,代码如下:
- package com.stucture.list.linkList;
- import com.stucture.list.IList;
- /**
- * 单链表
- * @ClassName: LinkList
- * @author 小学徒
- * @date 2013-2-27
- */
- public class LinkList<T> implements IList<T>{
- private Node<T> head; //链表的结点
- private int length; //链表的长度
- public LinkList(Node<T> head) {
- this.head = head;
- }
- //获取元素
- public T getElem(int loc) {
- int j = 1; //计数器
- Node<T> n = head; //指向第一个结点
- while(n != null) { //n不为空时,循环继续寻找第loc个结点
- if(j == loc) { //找到第一个元素时返回
- return n.getData();
- }
- n = n.getNext();
- j++;
- }
- return null;
- }
- //插入元素
- public boolean insertElem(int loc, T t) {
- if(length + 1 < loc) {
- System.out.println("非法插入");
- return false;
- }
- if(head == null && loc == 1) { //当第一次插入的时候
- head = new Node<T>(); //第一次使用,必须创建对象
- head.setData(t);
- length++;
- } else if(head != null && loc == 1) { //但不是第一次插入,但是插入的位置是第一个时
- Node<T> tempNode = new Node<T>(); //生成一个新的结点
- tempNode.setData(t);
- tempNode.setNext(head);
- head = tempNode; //把头换成新插入的结点
- length++;
- } else { //当不是第一次插入并且插入的不是第一个时
- Node<T> n = this.head;
- int j = 1; //计数器
- while(n != null && j < loc - 1) {
- n = n.getNext();
- j++;
- }
- Node<T> tempNode = new Node<T>(); //生成一个新的结点
- tempNode.setData(t);
- tempNode.setNext(n.getNext()); //将n的后继结点赋值给新的结点的后继
- n.setNext(tempNode);
- length++;
- }
- return true;
- }
- //删除元素
- public T deleteElem(int loc) {
- if(head == null || loc > length) {
- System.out.println("非法删除");
- return null;
- }
- T old;
- if(head != null && loc == 1) {
- old = head.getData();
- head = head.getNext();
- } else {
- Node<T> n = this.head;
- int j = 1; //计数器
- while(n != null && j < loc - 1) {
- n = n.getNext();
- j++;
- }
- old = n.getNext().getData();
- n.setNext(n.getNext().getNext());
- }
- length--;
- return old;
- }
- public Node<T> getHead() {
- return head;
- }
- public void setHead(Node<T> head) {
- this.head = head;
- }
- public int getLength() {
- return length;
- }
- public void setLength(int length) {
- this.length = length;
- }
- }
第四步,代码测试
- package com.stucture.list.linkList;
- import java.util.Random;
- public class LinkListTest {
- final int MAX = 25;
- Random r = new Random();
- LinkList<Integer> linkList;
- public LinkListTest() {
- initSeqList();
- }
- //创建一个线性表顺序存储结构
- public void initSeqList() {
- linkList = new LinkList<Integer>(null);
- int length = Math.abs(r.nextInt(MAX)); //使用Random随机产生一个25左右的值,使用Math.abs()函数来取绝对值
- System.out.println("产生的链表长度为 :" + length);
- for (int i = 1; i <= length; i++) { //为生成的链表赋值,同时也测试了插入值的方法
- int j =r.nextInt(MAX);
- System.out.print(j + " ");
- if(!linkList.insertElem(i, j)) {
- System.exit(0);
- }
- }
- System.out.println("\n原始链表是 :");
- display(linkList);
- }
- //测试删除方法
- public void deleteElem() {
- int i = r.nextInt(MAX);
- System.out.println("\n\n删除的位置是:" + i);
- Integer deleteNumber = linkList.deleteElem(i);
- if( deleteNumber == null) {
- System.exit(0);
- } else {
- System.out.println("删除的元素是 : " + deleteNumber);
- System.out.println("删除元素后链表是 :");
- display(linkList);
- }
- }
- //测试随机插入方法
- public void insertByRandom() {
- int i = r.nextInt(MAX);
- System.out.println("\n\n随机插入位置是 :" + i);
- int elem = r.nextInt(MAX);
- System.out.println("随机插入数据是 :" + elem);
- linkList.insertElem(i, elem);
- System.out.println("随机插入数据后链表是 :");
- display(linkList);
- }
- //数据展示
- public void display(LinkList<Integer> linkList) {
- Node<Integer> node = linkList.getHead();
- while(node != null) {
- System.out.print(node.getData() + " ");
- node = node.getNext();
- }
- System.out.println("链表的长度为 :" + linkList.getLength());
- }
- //获取元素
- public void getElem() {
- int i = r.nextInt(MAX);
- System.out.println("\n获取位置为 :" + i);
- System.out.println("获取到的元素为 : " + linkList.getElem(i));
- }
- public static void main(String[] args) {
- LinkListTest s = new LinkListTest();
- s.insertByRandom();
- s.deleteElem();
- s.getElem();
- }
- }
运行结果同样我不一一把各种结果列出啦,读者可以自己运行多几遍来进行学习:
- 产生的链表长度为 :23
- 5 19 18 12 6 12 15 19 16 21 13 16 5 4 18 9 9 18 17 13 16 6 17
- 原始链表是 :
- 5 19 18 12 6 12 15 19 16 21 13 16 5 4 18 9 9 18 17 13 16 6 17 链表的长度为 :23
- 随机插入位置是 :24
- 随机插入数据是 :0
- 随机插入数据后链表是 :
- 5 19 18 12 6 12 15 19 16 21 13 16 5 4 18 9 9 18 17 13 16 6 17 0 链表的长度为 :24
- 删除的位置是:11
- 删除的元素是 : 13
- 删除元素后链表是 :
- 5 19 18 12 6 12 15 19 16 21 16 5 4 18 9 9 18 17 13 16 6 17 0 链表的长度为 :23
- 获取位置为 :12
- 5 19 18 12 6 12 15 19 16 21 16 5 获取到的元素为 : 5
(java版之单向循环)
1. 单向循环链表:将单链表尾结点的指针端由空指针改为指向头结点,使整个单链表形成一个环,这种头尾相接的单链表称为单向循环链表。
2. 单向循环链表和单链表实现的区别:
1.)添加一个结点到单向循环链表末尾时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为null。
2.)判断是否到达表尾时,单向循环链表可以判断该结点是否指向头结点,单链表只需要知道是否为null。
3.java实现单向循环链表:
// 单向循环链表
public class CircularLinkedList<E> {
private Node<E> tail; // 尾结点
private int size; // 链表长度
public CircularLinkedList() {
tail = null;
size = 0;
}
// 在头结点前插入
public boolean addBeforeHead(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
tail = newNode;
tail.setNext(newNode); // 尾结点指向头结点
newNode.setNext(tail); // 头结点指向尾结点
}else{
Node<E> head = tail.getNext();
tail.setNext(newNode);
newNode.setNext(head);
}
size++;
return true;
}
// 在尾结点后插入
public boolean addAfterTail(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
tail = newNode;
tail.setNext(newNode);
newNode.setNext(tail);
}else{
Node<E> head = tail.getNext(); // 获取头结点
tail.setNext(newNode); // 将原尾结点指向新结点
tail = newNode; // 将新节点设置为尾结点
newNode.setNext(head); // 将新尾结点指向头结点
}
size++;
return true;
}
// 在某位置上插入(position为结点位置,不是角标)
public boolean insert(int position,E data){
if(position >= 1 && (position <= size + 1)){
if(isEmpty() || position == 1){ // 在头结点前插入
addBeforeHead(data);
}else if(position == size + 1){ // 在尾结点后插入
addAfterTail(data);
}else{ // 在中间位置插入
Node<E> preNode = get(position - 1); // 获取position的前一结点
Node<E> originalNode = preNode.getNext(); // 获取未插入结点时position位置对应结点
Node<E> newNode = new Node<E>(data);
preNode.setNext(newNode);
newNode.setNext(originalNode);
size++;
return true;
}
}
return false;
}
// 删除对应位置的结点
public E delete(int position){
E result = null;
if(position >= 1 && position <= size){
if(position == 1){ // 删除头结点
result = tail.getNext().getData();
Node<E> afterHead = tail.getNext().getNext();
tail.setNext(afterHead);
}else if(position == size){ // 删除尾结点
result = tail.getData();
Node<E> preTail = get(position - 1);
preTail.setNext(tail.getNext());
tail = preTail;
size--;
}else{ // 删除其他结点
Node<E> preNode = get(position - 1);
Node<E> curNode = preNode.getNext();
result = curNode.getData();
preNode.setNext(curNode.getNext());
size--;
}
}
return result;
}
// 获取某个位置的结点
public Node<E> get(int position){
Node<E> targetNode = null;
if(!isEmpty() && position >= 1 && position <= size){
targetNode = tail.getNext(); // 获取头结点
for(int i = 1; i < position ; i++){
targetNode = targetNode.getNext(); // 循环获取对应位置的结点
}
}
return targetNode;
}
// 获取链表的长度
public int getSize(){
return size;
}
// 判断链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 打印链表中的数据
public void display(){
Node<E> node = tail.getNext(); // 获取头结点
System.out.print("单向循环链表: ");
for(int i = 0; i < size; i++){
System.out.print(" " + node.getData());
node = node.getNext();
}
System.out.println("");
}
}
// 结点类,包含结点的数据和指向下一个节点的引用
public class Node<E> {
private E data; // 数据域
private Node<E> next; // 指针域保存着下一节点的引用
public Node() {
}
public Node(E data) {
this.data = data;
}
public Node(E data, Node<E> next) {
this.data = data;
this.next = next;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
public Node<E> getNext() {
return next;
}
public void setNext(Node<E> next) {
this.next = next;
}
}
// 测试类
public class Main {
public static void main(String[] args) {
CircularLinkedList<Integer> circular = new CircularLinkedList<Integer>();
circular.addBeforeHead(3);
circular.addBeforeHead(2);
circular.addBeforeHead(1);
circular.addAfterTail(4);
circular.addAfterTail(5);
circular.addAfterTail(6);
circular.insert(1,0);
circular.insert(8,7);
circular.insert(5,8);
circular.delete(5);
circular.display();
System.out.println("链表的长度为: " + circular.getSize());
}
}
(java版之双向)
1. 双向链表:在单链表的每个结点中,再设置一个指向其前驱结点的指针域,那么在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。
2. 单链表和双向链表比较:
单链表:总是要从头到尾找结点,只能正遍历,不能反遍历。
双向链表: 可以从头找到尾,也可以从尾找到头,即正反遍历都可以,可以有效提高算法的时间性能,但由于每个结点需要记录两份指针,所以在空间占用上略多一点,这就是通过空间来换时间。
3. Java实现双向链表:
// 双向链表
public class DoubleLinkedList<E> {
private Node<E> head; // 头结点
private int size; // 链表长度
// 建立一个空链表
public DoubleLinkedList() {
head = null;
size = 0;
}
// 在头结点前插入
public boolean addBeforeHead(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
head = newNode;
}else{
head.setPrev(newNode);
newNode.setNext(head);
head = newNode;
}
size++;
return true;
}
// 在链表尾部插入
public boolean addAfterTail(E data){
Node<E> newNode = new Node<E>(data);
if(isEmpty()){
head = newNode;
}else{
Node<E> tail = get(size);
tail.setNext(newNode);
newNode.setPrev(tail); // 设置新结点的上一结点
}
size++;
return true;
}
// 插入指定位置的结点
public boolean insert(int position, E data) {
if(position >= 1 && (position <= size + 1)){
Node<E> newNode = new Node<E>(data);
if(isEmpty() || position == 1){ // 链表为空或在头结点前插入
addBeforeHead(data);
}else if(position == size + 1){ // 在尾结点后插入
Node<E> preNode = get(position - 1);
newNode.setPrev(preNode);
preNode.setNext(newNode);
}else{ // 在其他位置插入
Node<E> preNode = get(position - 1); // 获取position的前一结点
Node<E> afterNode = preNode.getNext(); // 获取未插入结点时position位置对应结点
newNode.setPrev(preNode); // ①
newNode.setNext(afterNode); // ②
afterNode.setPrev(newNode); // ③
preNode.setNext(newNode); // ④
}
size++;
return true;
}
return false;
}
// 删除指定位置的结点
public E delete(int position) {
E result = null;
if(position >= 1 && position <= size){
if(position == 1){ // 删除头结点
result = head.getData();
Node<E> afterHead = head.getNext();
afterHead.setPrev(null);
head.setNext(null);
head = afterHead;
}else if(position == size){ // 删除尾结点
Node<E> preNode = get(position - 1); // 获取待删除结点的前一结点
Node<E> delNode = preNode.getNext(); // 获取待删除结点
result = delNode.getData();
preNode.setNext(null);
}else{ // 删除其他结点
Node<E> preNode = get(position - 1); // 获取待删除结点的前一结点
Node<E> delNode = preNode.getNext(); // 获取待删除结点
result = delNode.getData();
Node<E> nextNode = delNode.getNext();// 获取待删除结点的下一结点
preNode.setNext(nextNode); // ①
nextNode.setPrev(preNode); // ②
}
size--;
}
return result;
}
// 获取某个位置的结点(正序遍历)
public Node<E> get(int position){
Node<E> targetNode = null;
if(!isEmpty() && position >= 1 && position <= size){
targetNode = head;
for(int i = 1; i < position ; i++){
targetNode = targetNode.getNext(); // 循环获取对应位置的结点
}
}
return targetNode;
}
// 获取链表的长度
public int getSize(){
return size;
}
// 判断链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 打印链表数据
public void display(){
Node<E> node = head;
System.out.print("双向链表: ");
for(int i = 0; i < size; i++){
System.out.print(" " + node.getData());
node = node.getNext();
}
System.out.println("");
}
}
//结点类,包含结点的数据和指向下一个节点的引用
public class Node<E> {
private E data; // 数据域
private Node<E> next; // 指针域保存着下一节点的引用
private Node<E> prev; // 指针域保存着上一节点的引用 (相比单链表,双向链表多了这个指针)
public Node() {
}
public Node(E data) {
this.data = data;
}
public Node(E data, Node<E> next, Node<E> prev) {
this.data = data;
this.next = next;
this.prev = prev;
}
public E getData() {
return data;
}
public void setData(E data) {
this.data = data;
}
public Node<E> getNext() {
return next;
}
public void setNext(Node<E> next) {
this.next = next;
}
public Node<E> getPrev() {
return prev;
}
public void setPrev(Node<E> prev) {
this.prev = prev;
}
}
public class Main {
public static void main(String[] args) {
DoubleLinkedList<Integer> dll = new DoubleLinkedList<Integer>();
dll.addBeforeHead(2);
dll.addAfterTail(3);
dll.addBeforeHead(1);
dll.display();
dll.insert(4,4);
dll.insert(5,5);
dll.insert(6,6);
dll.display();
dll.delete(6);
dll.delete(3);
dll.delete(1);
dll.display();
System.out.println("双向链表的长度为: " + dll.getSize());
}
}
(java版之静态单向)
1. 静态链表:用数组描述的链表叫静态链表,通常为方便数据插入,我们会把数组建的大一些。
2. 数组元素(node):由两个数据域组成(data,cursor)。数据域data用来存放数据元素,也就是通常我们要处理的数据;而cursor相当于单链表中的指针,存放该元素的后继在数组中的下标
3. java实现静态链表:
// 静态链表
class StaticLinkedList {
private int size;
private Node[] node = new Node[100];
// 用数组初始化静态链表
public StaticLinkedList(int arr[]) {
for (int i = 0; i < 100; i++) {
node[i] = new Node(); // 初始化100个结点对象(感觉性能不会很好)
}
for (int j = 0; j < arr.length; j++) {
node[j + 1].setData(arr[j]); // 第一个结点为头结点,头结点没有数据,只存索引
node[j].setCursor(j + 1);
}
size = arr.length;
}
// 在某位置插入值
public void insert(int index, int value) {
validateIndex(index);
int curIndex = node[index].getCursor(); // 获取待插入结点的前一个结点指针,它记住的是原待插入结点位置
node[size + 1].setData(value); // 第一个结点为头结点,所以新插入的结点为node[size + 1]
node[size + 1].setCursor(curIndex); // 让新插入的结点记住原位置结点角标
node[index].setCursor(size + 1); // 让原位置前一结点记住新插入结点角标
size++;
}
// 删除指定位置的值
public void delete(int index) {
validateIndex(index);
int curIndex = node[index].getCursor(); // 获取待删除节点的前一个结点指针,它记住的是待删除结点角标(注:第一个结点为头结点)
int nextIndex = node[curIndex].getCursor(); // 获取待删除节点指针,它记住的是待删除的下一个结点角标
node[index].setCursor(nextIndex); // 将待删除结点的前一个结点指针指向待删除结点的下一个结点角标
size--;
}
// 验证下标值是否合法,非法时抛出异常。
private void validateIndex(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("无效的下标:" + index);
}
}
// 输出所有元素
public void display() {
int nextIndex = node[0].getCursor(); // node[0] 为头结点,存的是下一个结点角标
int i = 0;
while (i < size) {
System.out.printf("%d\t", node[nextIndex].getData());
nextIndex = node[nextIndex].getCursor();
i++;
}
}
}
// 结点(数组元素)
class Node {
int data; // 记录存入的数据
int cursor; // 记录下一个数据的下标
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public int getCursor() {
return cursor;
}
public void setCursor(int cursor) {
this.cursor = cursor;
}
}
// 测试类
public class Main {
public static void main(String[] args) {
int arr[] = { 1, 3, 4, 5, 6 };
StaticLinkedList list = new StaticLinkedList(arr);
System.out.print("初始化:\n");
list.display();
System.out.print("\n在角标为1的位置插入2后:\n");
list.insert(1, 2);
list.display();
System.out.print("\n删除角标为5的结点后:\n");
list.delete(5);
list.display();
}
}
4. 静态链表的优越点:
优点:在插入和删除操作时,不需要移动元素,从而改进了在顺序存储结构中插入和删除操作需要移动大量元素的缺点。
缺点:没有解决连续存储分配带来表长难以确定的问题。
5. 总的来说,静态链表其实是为了给没有指针的高级语言设计的一种实现单链表的方法,一般很少用。
(C版之静态单链表之一)
// c1.h----线性表的静态单链表存储结构------
typedef int ElemType;
#define MAXSIZE 100 // 链表的最大长度
// 线性表的静态单链表存储结构
typedef struct{
ElemType data; //存储数据,数据域
int cur; //游标指示器cur,代替指针指示结点在数组中的位置
}component,SLinkList[MAXSIZE];
// c2.h -----线性表的静态单链表的基本操作------
// 先将L的最后一个单元L[MAXSIZE-1]为表头,构造一个空的链表,然后将其余单元链成一个备用链表,这个
//备用链表的表头为L的第一个单元L[0],无数据域。“0”表示空指针,初始化各元素的游标指示器cur
void InitList(SLinkList L){
int i;
// L的最后一个单元为空链表的表头,无数据域
L[MAXSIZE-1].cur=0;
// 将其余单元链接成以L[0]为表头的备用链表
for(i=0;i<MAXSIZE-2;i++)
L[i].cur=i+1;
//这个为什么也置为零呢?因为它是那个一L[0]为表头的备用链表的尾结点
L[MAXSIZE-2].cur=0;
}
// 将L重置为空表
int ClearList(SLinkList L){
int i,j,k;
i=L[MAXSIZE-1].cur; // 链表第一个结点的位置
L[MAXSIZE-1].cur=0; // 链表置空空
k=L[0].cur; // 备用链表第一个结点的位置
L[0].cur=i; // 把链表的结点连到备用链表的表头
while(i) // 没到链表尾,继续循环
{
j=i;
i=L[i].cur; // 指向下一个元素
}
L[j].cur=k; // 备用链表的第一个结点接到链表的尾部
return 1;
}
// 若L是空表(第一个结点在数组的第0个位置,则是空表),返回1;否则返回0
int ListEmpty(SLinkList L){
if(L[MAXSIZE-1].cur==0) // 空表
return 1;
else
return 0;
}
// 返回L中数据元素个数
int ListLength(SLinkList L){
int j=0, //用来统计结点的个数
i=L[MAXSIZE-1].cur; // i指向第一个元素
while(i) // 没到静态链表尾,继续循环
{
i=L[i].cur; // 指向下一个元素
j++;
}
return j;
}
// 用e返回L中第i个元素的值
int GetElem(SLinkList L,int i,ElemType *e){
int l,
k=MAXSIZE-1; // k指向表头序号
if(i<1||i>ListLength(L))
return 0;
for(l=1;l<=i;l++) // 移动到第i个元素处
k=L[k].cur;
*e=L[k].data;
return 1;
}
// 算法2.13 P32
// 在静态单链线性表L中查找第1个值为e的元素。若找到,则返回它在L中的
// 位序,否则返回0。(与其它LocateElem()的定义不同)
int LocateElem(SLinkList L,ElemType e) {
int i=L[MAXSIZE-1].cur; // i指示表中第一个结点
while(i&&L[i].data!=e) // 在表中顺链查找(e不能是字符串)
i=L[i].cur;
return i;
}
// 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱
int PriorElem(SLinkList L,ElemType cur_e,ElemType *pre_e){
int j, //用来存储i所对应元素的前驱结点的位置
i=L[MAXSIZE-1].cur; // i指示链表第一个结点的位置
do
{ // 向后移动结点
j=i;
i=L[i].cur;
}while(i&&cur_e!=L[i].data);
if(i) // 找到该元素
{
*pre_e=L[j].data;
return 1;
}
return 0;
}
// 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
int NextElem(SLinkList L,ElemType cur_e,ElemType *next_e){
int j,
i=LocateElem(L,cur_e); // 在L中查找第一个值为cur_e的元素的位置
if(i) // L中存在元素cur_e
{
j=L[i].cur; // cur_e的后继的位置
if(j) // cur_e有后继
{
*next_e=L[j].data;
return 1; // cur_e元素有后继
}
}
return 0; // L不存在cur_e元素,cur_e元素无后继
}
// 算法2.15
// 若备用链表非空,则返回分配的结点下标(备用链表的第一个结点),否则返回0
int Malloc(SLinkList space)
{
int i=space[0].cur;
if(i) // 备用链表非空
// 备用链表的头结点指向原备用链表的第二个结点,因为第一个结点
// 已经被覆盖了
space[0].cur=space[i].cur;
return i; // 返回新开辟结点的坐标
}
// 算法2.16
// 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
void Free(SLinkList space,int k){
// 回收结点的"游标"指向备用链表的第一个结点
space[k].cur=space[0].cur;
space[0].cur=k; // 备用链表的头结点指向新回收的结点
}
void DestroyList(){
// 静态数组不能被销毁
}
// 在L中第i个元素之前插入新的数据元素e
int ListInsert(SLinkList L,int i,ElemType e){
int l,j,k=MAXSIZE-1; // k指向表头
if(i<1||i>ListLength(L)+1)
return 0;
j=Malloc(L); // 申请新单元
if(j) // 申请成功
{
L[j].data=e; // 赋值给新单元
for(l=1;l<i;l++) // 移动i-1个元素
k=L[k].cur;
L[j].cur=L[k].cur;
L[k].cur=j;
return 1;
}
return 0;
}
// 删除在L中第i个数据元素e,并返回其值
int ListDelete(SLinkList L,int i,ElemType *e){
int j,k=MAXSIZE-1; // k指向表头
if(i<1||i>ListLength(L))
return 0;
for(j=1;j<i;j++) // 移动i-1个元素
k=L[k].cur;
j=L[k].cur;
L[k].cur=L[j].cur;
*e=L[j].data;
Free(L,j);
return 1;
}
// 依次对L的每个数据元素调用函数vi()
int ListTraverse(SLinkList L,void(*vi)(ElemType)){
int i=L[MAXSIZE-1].cur; // 指向第一个元素
while(i) // 没到静态链表尾
{
vi(L[i].data); // 调用vi()
i=L[i].cur; // 指向下一个元素
}
printf("\n");
return 1;
}
void visit(ElemType c)
{
printf("%d ",c);
}
//main (检验基本操作的主程序)
#include <stdio.h>
#include <stdlib.h>
#include"c1.h"
#include"c2.h"
int main()
{
int j,k;
int i;
ElemType e,e0;
SLinkList L;
InitList(L);
for(j=1;j<=5;j++)
i=ListInsert(L,1,j);
printf("在L的表头依次插入1~5后:L=");
ListTraverse(L,visit);
i=ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n",i,ListLength(L));
i=ClearList(L);
printf("清空L后:L=");
ListTraverse(L,visit);
i=ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)表L的长度=%d\n",i,ListLength(L));
for(j=1;j<=10;j++)
ListInsert(L,j,j);
printf("在L的表尾依次插入1~10后:L=");
ListTraverse(L,visit);
GetElem(L,5,&e);
printf("第5个元素的值为:%d\n",e);
for(j=0;j<=1;j++)
{
k=LocateElem(L,j);
if(k)
printf("值为%d的元素在静态链表中的位序为%d\n",j,k);
else
printf("没有值为%d的元素\n",j);
}
for(j=1;j<=2;j++) // 测试头两个数据
{
GetElem(L,j,&e0); // 把第j个数据赋给e0
i=PriorElem(L,e0,&e); // 求e0的前驱
if(!i)
printf("元素%d无前驱\n",e0);
else
printf("元素%d的前驱为:%d\n",e0,e);
}
for(j=ListLength(L)-1;j<=ListLength(L);j++) // 最后两个数据
{
GetElem(L,j,&e0); // 把第j个数据赋给e0
i=NextElem(L,e0,&e); // 求e0的后继
if(!i)
printf("元素%d无后继\n",e0);
else
printf("元素%d的后继为:%d\n",e0,e);
}
k=ListLength(L); // k为表长
for(j=k+1;j>=k;j--)
{
i=ListDelete(L,j,&e); // 删除第j个数据
if(i)
printf("删除的元素为:%d\n",e);
else
printf("删除第%d个数据失败\n",j);
}
printf("依次输出L的元素:");
ListTraverse(L,visit); // 依次对元素调用visit(),输出元素的值
system("pause");
return 0;
}
-------------------------------------------------------------------------------------------------
输出效果:
在L的表头依次插入1~5后:L=5 4 3 2 1
L是否空:i=0(1:是 0:否)表L的长度=5
清空L后:L=
L是否空:i=1(1:是 0:否)表L的长度=0
在L的表尾依次插入1~10后:L=1 2 3 4 5 6 7 8 9 10
第5个元素的值为:5
没有值为0的元素
值为1的元素在静态链表中的位序为5
元素1无前驱
元素2的前驱为:1
元素9的后继为:10
元素10无后继
删除第11个数据失败
删除的元素为:10
依次输出L的元素:1 2 3 4 5 6 7 8 9
请按任意键继续. . .
(C版之单链表存储之二)
// c1.h 线性表的单链表存储结构
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
// c2.h 线性表的单链表的基本操作
// 构造一个空的线性表L
int InitList(LinkList *L){
//产生头结点L,并使L指向此头结点,头节点的数据域为空,不放数据的。
//void * malloc(size_t)
//这里对返回值进行强制类型转换了,返回值是指向空类型的指针类型。
(*L) = (LinkList)malloc( sizeof(struct LNode) );
if( !(*L) )
exit(0); // 存储分配失败
(*L)->next = NULL; // 指针域为空
return 1;
}
// 销毁线性表L,将包括头结点在内的所有元素释放其存储空间。
int DestroyList(LinkList *L){
LinkList q;
// 由于单链表的每一个元素是单独分配的,所以要一个一个的进行释放
while( *L ) {
q = (*L)->next;
free( *L ); //释放
*L = q;
}
return 1;
}
//将L重置为空表,即将链表中除头结点外的所有元素释放其存储空间
//但是将头结点指针域置空,这和销毁有区别哦。不改变L,所以不需要用指针。
int ClearList( LinkList L ){
LinkList p, q;
p = L->next; // p指向第一个结点
while( p ) // 没到表尾则继续循环
{
q = p->next;
free( p ); //释放空间
p = q;
}
L->next = NULL; // 头结点指针域为空,链表成了一个空表
return 1;
}
// 若L为空表(根据头结点L->next来判断,为空则是空表),则返回1, 否则返回0。
int ListEmpty(LinkList L){
if( L->next ) // 非空
return 0;
else
return 1;
}
// 返回L中数据元素个数。
int ListLength(LinkList L){
int i = 0;
LinkList p = L->next; // p指向第一个结点
while(p) // 没到表尾,则继续循环
{
i++;
p=p->next;
}
return i;
}
// 算法2.8 P29
// L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回1,否则返回0。
int GetElem(LinkList L,int i,ElemType *e){
int j = 1; // j为计数器
LinkList p=L->next; // p指向第一个结点
while(p&&j<i) // 顺指针向后查找,直到p指向第i个元素或p为空
{
p=p->next;
j++;
}
if(!p||j>i) // 第i个元素不存在
return 0;
*e = p->data; // 取第i个元素
return 1;
}
// 返回L中第1个与e满足关系compare()的数据元素的位序。 若这样的数据元素不存在,则返回值为0
int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType)){
int i=0;
LinkList p=L->next;
while(p) //将链表的每一个元素进行对比
{
i++;
if(compare(p->data,e)) // 找到这样的数据元素
return i;
p=p->next;
}
return 0;
}
// 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
// 返回1;否则操作失败,pre_e无定义,返回-1
int PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e){
LinkList q,
p=L->next; // p指向第一个结点
while(p->next) // p所指结点有后继
{
q=p->next; // q为p的后继
if(q->data==cur_e)
{
*pre_e=p->data;
return 1;
}
p=q; // p向后移
}
return -1;
}
// 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,
// 返回1;否则操作失败,next_e无定义,返回-1
int NextElem(LinkList L,ElemType cur_e,ElemType *next_e){
LinkList p=L->next; // p指向第一个结点
while(p->next) // p所指结点有后继
{
if(p->data==cur_e){
*next_e=p->next->data;
return 1;
}
p=p->next;
}
return -1;
}
// 算法2.9 P30
// 在带头结点的单链线性表L中第i个位置之前插入元素e
int ListInsert(LinkList *L,int i,ElemType e){
int j=0;
LinkList p=*L,s;
while(p && j<i-1) // 寻找第i-1个结点
{
p=p->next;
j++;
}
if(!p || j>i-1) // i小于1或者大于表长
return 0;
s=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
s->data=e; // 插入L中
s->next=p->next;
p->next=s;
return 1;
}
// 算法2.10 P30
// 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
int ListDelete(LinkList *L, int i,ElemType *e){
int j = 0;
LinkList p=*L,q;
while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
{
p=p->next;
j++;
}
if(!p->next||j>i-1) // 删除位置不合理
return 0;
q=p->next; // 删除并释放结点
p->next=q->next;
*e=q->data;
free(q);
return 1;
}
// 依次对L的每个数据元素调用函数vi()
int ListTraverse(LinkList L,void(*vi)(ElemType)){
LinkList p=L->next;
//对所有元素调用函数vi
while(p)
{
vi(p->data);
p=p->next;
}
printf("\n");
return 1;
}
// 在按非降序排列的线性表L中按非降序插入新的数据元素e
void InsertAscend(LinkList L,ElemType e){
LinkList q=L,
p=L->next;
while(p&&e>p->data)
{
q=p;
p=p->next;
}
q->next=(LinkList)malloc(sizeof(struct LNode)); // e插在q后
q->next->data=e;
q->next->next=p;
}
// 按非升序排列的线性表L中按非升序插入新的数据元素e
void InsertDescend(LinkList L,ElemType e){
LinkList q=L,p=L->next;
while(p&&e<p->data)
{
q=p;
p=p->next;
}
q->next=(LinkList)malloc(sizeof(struct LNode)); // e插在q后
q->next->data=e;
q->next->next=p;
}
// L的头部插入新的数据元素e,作为链表的第一个元素
int HeadInsert(LinkList L,ElemType e){
LinkList s;
s=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
s->data=e; // 给结点赋值
s->next=L->next; // 插在表头
L->next=s;
return 1;
}
// 在L的尾部插入新的数据元素e,作为链表的最后一个元素
int EndInsert(LinkList L,ElemType e){
LinkList p=L;
while(p->next) // 使p指向表尾元素
p=p->next;
p->next=(LinkList)malloc(sizeof(struct LNode)); // 在表尾生成新结点
p->next->data=e; // 给新结点赋值
p->next->next=NULL; // 表尾
return 1;
}
// 删除L的第一个数据元素,并由e返回其值
int DeleteFirst(LinkList L,ElemType *e){
LinkList p=L->next;
if(p)
{
*e=p->data;
L->next=p->next;
free(p);
return 1;
}
else
return 0;
}
// 删除L的最后一个数据元素,并用e返回其值
int DeleteTail(LinkList L,ElemType *e)
{
LinkList p=L,q;
if(!p->next) // 链表为空
return 0;
while(p->next)
{
q=p;
p=p->next;
}
q->next=NULL; // 新尾结点的next域设为NULL
*e=p->data;
free(p);
return 1;
}
// 删除表中值为e的元素,并返回1;如无此元素,则返回0
int DeleteElem(LinkList L,ElemType e){
LinkList p=L,q;
while(p)
{
q=p->next;
if(q&&q->data==e)
{
p->next=q->next;
free(q);
return 1;
}
p=q;
}
return 0;
}
// 用e取代表L中第i个元素的值
int ReplaceElem(LinkList L,int i,ElemType e){
LinkList p=L;
int j=0;
//找到第i个元素的位置给p
while(p->next && j<i)
{
j++;
p=p->next;
}
if(j==i)
{
p->data=e;
return 1;
}
else // 表中不存在第i个元素
return 0;
}
// 按非降序建立n个元素的线性表
int CreatAscend(LinkList *L,int n){
int j;
LinkList p,q,s;
if(n<=0)
return 0;
InitList(L);
printf("请输入%d个元素:(空格)\n",n);
s=(LinkList)malloc(sizeof(struct LNode)); // 第一个结点
scanf("%d",&s->data);
s->next=NULL;
(*L)->next=s;
for(j=1;j<n;j++)
{
s=(LinkList)malloc(sizeof(struct LNode)); // 其余结点
scanf("%d",&s->data);
q=*L;
p=(*L)->next;
while(p&&p->data<s->data) // p没到表尾,且所指元素值小于新值
{
q=p;
p=p->next; // 指针后移
}
s->next=q->next; // 元素插在q的后面
q->next=s;
}
return 1;
}
// 按非升序建立n个元素的线性表
int CreatDescend(LinkList *L,int n)
{
int j;
LinkList p,q,s;
if(n<=0)
return 0;
InitList(L);
printf("请输入%d个元素:(空格)\n",n);
s=(LinkList)malloc(sizeof(struct LNode)); // 第一个结点
scanf("%d",&s->data);
s->next=NULL;
(*L)->next=s;
for(j=1;j<n;j++)
{
s=(LinkList)malloc(sizeof(struct LNode)); // 其余结点
scanf("%d",&s->data);
q=*L;
p=(*L)->next;
while(p&&p->data>s->data) // p没到表尾,且所指元素值大于新值
{
q=p;
p=p->next; // 指针后移
}
s->next=q->next; // 元素插在q的后面
q->next=s;
}
return 1;
}
// 返回表头元素的值
int GetFirstElem(LinkList L,ElemType *e)
{
LinkList p=L->next; //第一个结点给p
if(!p) // 空表
return 0;
else // 非空表
*e=p->data;
return 1;
}
// 算法2.11 P30
// 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L
void CreateList(LinkList *L,int n){
int i;
LinkList p;
// 先建立一个带头结点的空单链表,相当于初始化单链表
*L=(LinkList)malloc(sizeof(struct LNode));
(*L)->next=NULL;
printf("请输入%d个数据\n",n);
for(i=n;i>0;--i)
{
p=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
scanf("%d",&p->data); // 输入元素值
p->next=(*L)->next; // 插入到表头
(*L)->next=p;
}
}
// 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表
void CreateList2(LinkList *L,int n){
int i;
LinkList p,q;
// 先建立一个带头结点的空单链表,相当于初始化单链表
*L=(LinkList)malloc(sizeof(struct LNode)); // 生成头结点
(*L)->next=NULL;
q=*L;
printf("请输入%d个数据\n",n);
for(i=1;i<=n;i++)
{
p=(LinkList)malloc(sizeof(struct LNode));
scanf("%d",&p->data);
q->next=p;
q=q->next;
}
p->next=NULL;
}
--------------------------------------------------------------------------------------------
用单链表重写 算法2.2 供参考
已知线性表La和Lb中的数据元素按值非递减排列。
归并La和Lb得到新的线性表Lc,Lc的数据元素也按值非递减排列
void MergeList(LinkList La,LinkList Lb,LinkList *Lc)
{
int i=1,j=1,k=0;
int La_len,Lb_len;
ElemType ai,bj;
InitList(Lc);
La_len=ListLength(La);
Lb_len=ListLength(Lb);
while(i<=La_len&&j<=Lb_len) // 表La和表Lb均非空
{
GetElem(La,i,&ai);
GetElem(Lb,j,&bj);
if(ai<=bj)
{
ListInsert(Lc,++k,ai);
++i;
}
else
{
ListInsert(Lc,++k,bj);
++j;
}
}
while(i<=La_len) // 表La非空且表Lb空
{
GetElem(La,i++,&ai);
ListInsert(Lc,++k,ai);
}
while(j<=Lb_len) // 表Lb非空且表La空
{
GetElem(Lb,j++,&bj);
ListInsert(Lc,++k,bj);
}
}
----------------------------------------------------------------------------------------------
// 算法2.12 P31
// 已知单链线性表La和Lb的元素按值非递减排列。
// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
void MergeList(LinkList La,LinkList *Lb,LinkList *Lc) {
LinkList pa=La->next,pb=(*Lb)->next,pc;
*Lc=pc=La; // 用La的头结点作为Lc的头结点
while(pa&&pb){
if(pa->data <= pb->data){
pc->next=pa;
*Lc=pa;
pa=pa->next;
}
else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa ? pa : pb; // 插入剩余段
free(*Lb); // 释放Lb的头结点
Lb=NULL;
}
// 判断是否相等的函数,Union()用到
int equal(ElemType c1,ElemType c2)
{
if(c1==c2)
return 1;
else
return 0;
}
// 算法2.1
// 将所有在线性表Lb中但不在La中的数据元素插入到La中
void Union(LinkList La,LinkList Lb){
ElemType e;
int La_len,Lb_len;
int i;
La_len=ListLength(La); // 求线性表的长度
Lb_len=ListLength(Lb);
for(i=1;i<=Lb_len;i++)
{
GetElem(Lb,i,&e); // 取Lb中第i个数据元素赋给e
if(!LocateElem(La,e,equal)) // La中不存在和e相同的元素,则插入之
ListInsert(&La,++La_len,e);
}
}
// 数据元素判定函数(相等为1,否则为0)
int comp(ElemType c1,ElemType c2)
{
if(c1==c2)
return 1;
else
return 0;
}
void visit(ElemType c)
{
printf("%d ",c);
}
//main (检验基本操作的主程序)
typedef int ElemType;
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include "c1.h"
#include "c2.h"
int main()
{
LinkList L, La, Lb, Lc;
ElemType e, e0, d;
int i, j, n, k;
//初始化一个单链表
i=InitList(&L);
//通过插入操作创建一个单链表
for(j=1;j<=5;j++)
i=ListInsert(&L,1,j);
//调用visit函数,对单链表进行遍历
printf("在L的表头依次插入1~5后:L=");
ListTraverse(L,visit); // 依次对元素调用visit(),输出元素的值
//判断单链表是否为空
i=ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n",i);
//清空单链表
i=ClearList(L);
printf("清空L后:L=");
ListTraverse(L,visit);
//判断单链表是否为空
i=ListEmpty(L);
printf("L是否空:i=%d(1:是 0:否)\n",i);
//再次通过插入操作创建一个单链表
for(j=1;j<=10;j++)
ListInsert(&L,j,j);
printf("在L的表尾依次插入1~10后:L=");
ListTraverse(L,visit);
//取得单链表的第5个元素
GetElem(L,5,&e);
printf("第5个元素的值为:%d\n",e);
//在单链表中找到和j满足comp函数关系的元素
for(j=0;j<=1;j++)
{
k=LocateElem(L,j,comp);
if(k)
printf("第%d个元素的值为%d\n",k,j);
else
printf("没有值为%d的元素\n",j);
}
//找到某个元素的前驱
for(j=1;j<=2;j++) // 测试头两个数据
{
GetElem(L,j,&e0); // 把第j个数据赋给e0
i=PriorElem(L,e0,&e); // 求e0的前驱
if(i==-1)
printf("元素%d无前驱\n",e0);
else
printf("元素%d的前驱为:%d\n",e0,e);
}
//找到某个元素的后继
for(j=ListLength(L)-1;j<=ListLength(L);j++)// 测试最后两个数据
{
GetElem(L,j,&e0); // 把第j个数据赋给e0
i=NextElem(L,e0,&e); // 求e0的后继
if(i==-1)
printf("元素%d无后继\n",e0);
else
printf("元素%d的后继为:%d\n",e0,e);
}
//求单链表的表长
k=ListLength(L); // k为表长
//删除操作
for(j=k+1;j>=k;j--)
{
i=ListDelete(&L,j,&e); // 删除第j个数据
if(i==0)
printf("删除第%d个数据失败\n",j);
else
printf("删除的元素为:%d\n",e);
}
printf("依次输出L的元素:");
ListTraverse(L,visit);
//销毁单链表
DestroyList(&L);
printf("销毁L后:L=%u\n\n",L);
printf("按非降序建立n个元素的线性表L,请输入元素个数n: ");
scanf("%d",&n);
CreatAscend(&L,n);
printf("依次输出L的元素:");
ListTraverse(L,visit);
// 按非降序插入元素10
InsertAscend(L,10);
printf("按非降序插入元素10后,线性表L为:");
ListTraverse(L,visit);
// 在L的头部插入12
HeadInsert(L,12);
// 在L的尾部插入9
EndInsert(L,9);
printf("在L的头部插入12,尾部插入9后,线性表L为:");
ListTraverse(L,visit);
i=GetFirstElem(L,&e);
printf("第1个元素是: %d\n",e);
printf("请输入要删除的元素的值: ");
scanf("%d",&e);
i=DeleteElem(L,e);
if(i)
printf("成功删除%d!\n",e);
else
printf("不存在元素%d!\n",e);
printf("线性表L为:");
ListTraverse(L,visit);
printf("请输入要取代的元素的序号 元素的新值: ");
scanf("%d%d",&n,&e);
ReplaceElem(L,n,e);
printf("线性表L为:");
ListTraverse(L,visit);
DestroyList(&L);
printf("销毁L后,按非升序重新建立n个元素的线性表L,请输入"
"元素个数n(>2): ");
scanf("%d",&n);
CreatDescend(&L,n);
printf("依次输出L的元素:");
ListTraverse(L,visit);
// 按非升序插入元素10
InsertDescend(L,10);
printf("按非升序插入元素10后,线性表L为:");
ListTraverse(L,visit);
printf("请输入要删除的元素的值: ");
scanf("%d",&e);
i=DeleteElem(L,e);
if(i)
printf("成功删除%d!\n",e);
else
printf("不存在元素%d!\n",e);
printf("线性表L为:");
ListTraverse(L,visit);
DeleteFirst(L,&e);
DeleteTail(L,&d);
printf("删除表头元素%d和表尾元素%d后,线性表L为:",e,d);
ListTraverse(L,visit);
printf("\n");
// 测试算法2.11
n = 3;
CreateList2(&La,n); // 正位序输入n个元素的值
printf("正位创建后La="); // 输出链表La的内容
ListTraverse(La,visit);
CreateList(&Lb,n); // 逆位序输入n个元素的值
printf("逆位创建后Lb="); // 输出链表Lb的内容
ListTraverse(Lb,visit);
DestroyList(&La);
DestroyList(&Lb);
// 测试算法2.12
//初始化一个单链表La
i=InitList(&La);
//通过插入操作创建一个单链表
for(j=2;j<=10;j+=2)
i=ListInsert(&La,1,j);
printf("La="); // 输出链表La的内容
ListTraverse(La,visit);
//初始化一个单链表
i=InitList(&Lb);
//通过插入操作创建一个单链表
for(j=1;j<=10;j+=2)
i=ListInsert(&Lb,1,j);
printf("Lb="); // 输出链表Lb的内容
ListTraverse(Lb,visit);
// 按非递减顺序归并La和Lb,得到新表Lc
MergeList(La,&Lb,&Lc);
printf("合并La和Lb后,Lc = "); // 输出链表Lc的内容
ListTraverse(Lc,visit);
// 测试算法2.1
i=InitList(&La);
if(i==1) // 创建空表La成功
for(j=1;j<=5;j++) // 在表La中插入5个元素
i=ListInsert(&La,j,j);
printf("La= "); // 输出表La的内容
ListTraverse(La,visit);
InitList(&Lb); // 也可不判断是否创建成功
for(j=1;j<=5;j++) // 在表Lb中插入5个元素
i=ListInsert(&Lb,j,2*j);
printf("Lb= "); // 输出表Lb的内容
ListTraverse(Lb,visit);
Union(La,Lb);
printf("new La= "); // 输出新表La的内容
ListTraverse(La,visit);
system("pause");
return 0;
}
--------------------------------------------------------------------------------------------------
输出效果:
在L的表头依次插入1~5后:L=5 4 3 2 1
L是否空:i=0(1:是 0:否)
清空L后:L=
L是否空:i=1(1:是 0:否)
在L的表尾依次插入1~10后:L=1 2 3 4 5 6 7 8 9 10
第5个元素的值为:5
没有值为0的元素
第1个元素的值为1
元素1无前驱
元素2的前驱为:1
元素9的后继为:10
元素10无后继
删除第11个数据失败
删除的元素为:10
依次输出L的元素:1 2 3 4 5 6 7 8 9
销毁L后:L=0
按非降序建立n个元素的线性表L,请输入元素个数n: 3
请输入3个元素:(空格)
1 3 2
依次输出L的元素:1 2 3
按非降序插入元素10后,线性表L为:1 2 3 10
在L的头部插入12,尾部插入9后,线性表L为:12 1 2 3 10 9
第1个元素是: 12
请输入要删除的元素的值: 1
成功删除1!
线性表L为:12 2 3 10 9
请输入要取代的元素的序号 元素的新值: 3 4
线性表L为:12 2 4 10 9
销毁L后,按非升序重新建立n个元素的线性表L,请输入元素个数n(>2): 3
请输入3个元素:(空格)
1 3 2
依次输出L的元素:3 2 1
按非升序插入元素10后,线性表L为:10 3 2 1
请输入要删除的元素的值: 3
成功删除3!
线性表L为:10 2 1
删除表头元素10和表尾元素1后,线性表L为:2
请输入3个数据
1 3 2
正位创建后La=1 3 2
请输入3个数据
1 3 2
逆位创建后Lb=2 3 1
La=10 8 6 4 2
Lb=9 7 5 3 1
合并La和Lb后,Lc = 9 7 5 3 1 10 8 6 4 2
La= 1 2 3 4 5
Lb= 2 4 6 8 10
new La= 1 2 3 4 5 6 8 10
请按任意键继续. . .
(C版之单链表之三)
一、为什么要采用链式存储(链表)存在的意义 为什么要采用链式存储:
与数组相比,链式存储(即链表)有如下两个优点:
1、数据元素的个数不确定,随时可能增减。采用固定大小的数组浪费空间。
2、方便排序,对于数组来说,每次插入一个元素都可能导致大量数据的移动。
有缺点吗:
与素族相比,链式存储有一个很大的缺点——读取数据!
对于读取其中指定第N个数据,链表必须从头结点用p = p->next(头结点不存储数据);一直遍历N次或N-1次(头结点存储数据)。所以在需要频繁索取某些指定数据的情况下,牺牲空间为代价换取更优的性能就需要采取数组这种数据结构了。
二、链表的定义和操作
链表的基础——结构体和指针:
懂得结构体,懂得指针,那么学习链表就很简单了。
- /*
- * 头结点存储数据,即不带头结点的链表
- */
- #include <stdio.h>
- #include <stdlib.h>
- #define OverFlow -1 //定义OverFlow表示内存溢出
- #define OK 0 //定义OK表示成功
- #define Error -2 //定义操作失败的返回值
- #define OverFlow -1; //定义OverFlow表示内存溢出
- #define OK 0; //定义OK表示成功
- #define Error -2; //定义操作失败的返回值
- /*
- * 首先定义一个数据类型int的别名ElemType,
- * 增强程序的可移植性,注意typedef和define的区别
- */
- typedef int ElemType;
- /*
- * 紧接着定义链表的节点,其实就是>=1个包含数据
- * 的元素(类型任意)和一个本结构体类型的Next指
- * 针(其值指向链表的下一个节点的地址)
- */
- typedef struct node
- {
- ElemType data;
- struct node *next;
- } Node, *LinkList;
/* * 头结点存储数据,即不带头结点的链表 */#include <stdio.h>#include <stdlib.h>#define OverFlow -1 //定义OverFlow表示内存溢出#define OK 0 //定义OK表示成功#define Error -2 //定义操作失败的返回值#define OverFlow -1; //定义OverFlow表示内存溢出#define OK 0; //定义OK表示成功#define Error -2; //定义操作失败的返回值/* * 首先定义一个数据类型int的别名ElemType, * 增强程序的可移植性,注意typedef和define的区别 */typedef int ElemType;/* * 紧接着定义链表的节点,其实就是>=1个包含数据 * 的元素(类型任意)和一个本结构体类型的Next指 * 针(其值指向链表的下一个节点的地址) */typedef struct node{ElemType data;struct node *next;} Node, *LinkList;
定义了节点的结构体,我们来进行链表操作的函数编写:
首先来看头结点不存储数据的情况:
我们需要:
1.构造一个空表
构造空表分两种情况,构造头结点不存储数据的空表和头结点存储数据的空表。
- /*
- * 1.构建头结点不存储数据的空表(相对简单)
- * 注意函数参数传递的原理
- */
- void Init_LinkList(LinkList *Head_pointer)
- {
- *Head_pointer = NULL;
- }
/* * 1.构建头结点不存储数据的空表(相对简单) * 注意函数参数传递的原理 */void Init_LinkList(LinkList *Head_pointer) {*Head_pointer = NULL;}
2.插入一个元素(头插)
插入一个元素分三步:第一步,定义节点p并初始化,包括分配空间和赋值;第二步,找准插入位置,一般找到插入点的前一个元素;第三步,p->next赋值(这一定首先进行,进行此步骤不影响链表中任何信息)等一系列链表操作,这是核心部分。
- /*
- * 2.插入一个元素(头插)
- * 这时候不需要传入位置的数据,只需要传入头指针和数据
- */
- int Insert_First(LinkList *Head_pointer, ElemType x)
- {
- Node *p; //这里考虑为什么不用LinkList
- p = (Node *) malloc(sizeof Node);
- if (p == NULL)
- return OverFlow;
- p->data = x;
- p->next = *Head_pointer;
- *Head_pointer = p;
- return OK;
- }
/* * 2.插入一个元素(头插) * 这时候不需要传入位置的数据,只需要传入头指针和数据 */int Insert_First(LinkList *Head_pointer, ElemType x){Node *p; //这里考虑为什么不用LinkListp = (Node *) malloc(sizeof Node);if (p == NULL)return OverFlow;p->data = x;p->next = *Head_pointer;*Head_pointer = p;return OK;}
3.查找指定的元素(与数组查找元素效率差不多)
- /*
- * 3.查找指定元素,注意这里用到了LinkList定义数据
- * 因为不是要定义一个节点,只是定义一个指针
- */
- LinkList Location_LinkList(LinkList Head, ElemType x)
- {
- LinkList p;
- p = Head;
- while(p != NULL)
- {
- if (p->data == x)
- break;
- p = p->next;
- }
- return p;
- }
/* * 3.查找指定元素,注意这里用到了LinkList定义数据 * 因为不是要定义一个节点,只是定义一个指针 */LinkList Location_LinkList(LinkList Head, ElemType x){LinkList p;p = Head;while(p != NULL){if (p->data == x)break;p = p->next;}return p;}
4.删除指定的元素
这里要注意头结点就是要删除的元素时,操作代码不一样。建立链表时头结点不存入数据的原因就在这里。
- /*
- * 4.删除指定的元素
- * 有可能改变头结点的值,所以要传入指针
- * 对头结点就是要删除的元素进行单独处理
- */
- int Delete_LinkList(LinkList *Head_pointer, ElemType x)
- {
- Node *p, *q;
- p = *Head_pointer;
- if (p->data == x)//考虑头结点就是要删除的元素
- {
- *Head_pointer = (*Head_pointer)->next;
- free(p);
- return OK;
- }
- else
- {
- q = p; p = p->next; //q指向前一个节点,p指向下一个节点
- while(p != NULL)
- {
- if (p->data == x)
- {
- q->next = p->next;
- free(p);
- return OK;
- }
- q = p; p = p->next;
- }
- }
- return Error;
- }
/* * 4.删除指定的元素 * 有可能改变头结点的值,所以要传入指针 * 对头结点就是要删除的元素进行单独处理 */int Delete_LinkList(LinkList *Head_pointer, ElemType x){Node *p, *q;p = *Head_pointer;if (p->data == x)//考虑头结点就是要删除的元素{*Head_pointer = (*Head_pointer)->next;free(p);return OK;}else{q = p; p = p->next; //q指向前一个节点,p指向下一个节点while(p != NULL){if (p->data == x){q->next = p->next;free(p);return OK;}q = p; p = p->next;}}return Error;}
5.遍历链表
其实就是逐个操作链表,操作可以包括打印每个元素,更改每个元素。
注意如果在linux下面打印中文出现乱码的情况,请更改编码方式。可参考我在163博客中的一篇文章:http://canlynet.blog.163.com/blog/static/25501365200911300521926/
- /*
- * 5.遍历线性表,打印每个数据
- * 只需要传入Head的值即可
- * 头结点为空需要打印空表,在linux的超级终端下注意中文编码问题
- */
- void Show_LinkList(LinkList Head)
- {
- LinkList p = Head;
- int i = 0;
- printf("----链表打印----\n");
- if (p == NULL) //处理头结点为空的情况
- printf("空表\n");
- while (p != NULL)
- {
- printf("[%d]:%d\t", i++, p->data);
- p = p->next;
- }
- }
/* * 5.遍历线性表,打印每个数据 * 只需要传入Head的值即可 * 头结点为空需要打印空表,在linux的超级终端下注意中文编码问题 */void Show_LinkList(LinkList Head){LinkList p = Head;int i = 0;printf("----链表打印----\n");if (p == NULL) //处理头结点为空的情况printf("空表\n");while (p != NULL){printf("[%d]:%d\t", i++, p->data);p = p->next;}}
6.清空链表
清空链表需要传入头结点指针。
- /*
- * 6.清空链表
- * 清除到头结点为空的状态,也就是一个空表的状态
- */
- void SetNull_LinkList(LinkList *Head_pointer)
- {
- LinkList p, q;
- p = *Head_pointer;
- while (p != NULL)
- {
- q = p;
- p = p->next;
- free(q);
- }
- }
/* * 6.清空链表 * 清除到头结点为空的状态,也就是一个空表的状态 */void SetNull_LinkList(LinkList *Head_pointer){LinkList p, q;p = *Head_pointer;while (p != NULL){q = p;p = p->next;free(q);}}
7.计算链表的长度
注意算法:从Head计算,指针不为空的总数
/*
* 7.计算链表的长度
* 计算方法:从Head开始,计算指针不为空的个数
*/
int Length_LinkList(LinkList Head)
{
LinkList p = Head;
int sum = 0;
while(p != NULL)
{
sum++;
p = p->next;
}
return sum;
}
8.调用单链表操作的主函数
看了下面这个主函数,我们基本上能够感受到c语言编写软件的一种方式。
- /*
- *8.调用单链表操作的主函数
- */
- int main(void)
- {
- LinkList Head;
- int i;
- Node *loca;
- ElemType x;
- Init_LinkList(&Head);
- do
- {
- printf("\n");
- printf("1---插入一个元素(Insert)\n");
- printf("2---查询一个元素(Locate)\n");
- printf("3---删除一个元素(Delete)\n");
- printf("4---显示所有元素(Show)\n");
- printf("5---计算表的长度(Length)\n");
- printf("6---退出\n");
- scanf("%d", &i);
- switch (i)
- {
- case 1: printf("请输入要插入的分数:\n");
- scanf("%d", &x);
- if (Insert_First(&Head, x) != OK)
- printf("插入失败\n");
- break;
- case 2: printf("请输入要查询的分数\n");
- loca = Location_LinkList(Head, x);
- if (loca != NULL)
- printf("查询成功\n");
- else
- printf("查询失败\n");
- break;
- case 3: printf("请输入要删除的分数\n");
- scanf("%d", &x);
- if (Delete_LinkList(&Head, x) != OK)
- printf("删除失败\n");
- else
- printf("删除成功\n");
- break;
- case 4: Show_LinkList(Head);
- break;
- case 5: printf("表的长度是:%d", Length_LinkList(Head));
- break;
- case 6: break;
- default: printf("错误选择!请重选");
- break;
- }
- } while (i != 6);
- SetNull_LinkList(&Head);
- printf("链表已清空,程序退出...\n");
- return 0;
- }
/* *8.调用单链表操作的主函数 */int main(void){LinkList Head;int i;Node *loca;ElemType x;Init_LinkList(&Head);do{printf("\n");printf("1---插入一个元素(Insert)\n");printf("2---查询一个元素(Locate)\n");printf("3---删除一个元素(Delete)\n");printf("4---显示所有元素(Show)\n");printf("5---计算表的长度(Length)\n");printf("6---退出\n");scanf("%d", &i);switch (i){case 1:printf("请输入要插入的分数:\n");scanf("%d", &x);if (Insert_First(&Head, x) != OK)printf("插入失败\n");break;case 2:printf("请输入要查询的分数\n");loca = Location_LinkList(Head, x);if (loca != NULL)printf("查询成功\n");elseprintf("查询失败\n");break;case 3:printf("请输入要删除的分数\n");scanf("%d", &x);if (Delete_LinkList(&Head, x) != OK)printf("删除失败\n");elseprintf("删除成功\n");break;case 4:Show_LinkList(Head);break;case 5:printf("表的长度是:%d", Length_LinkList(Head));break;case 6:break;default:printf("错误选择!请重选");break;}} while (i != 6);SetNull_LinkList(&Head);printf("链表已清空,程序退出...\n");return 0;}
(c++版之双向循环)
#include<iostream>
using
namespace
std;
template
<
typename
T>
struct
node{
T data;
node<T> *prior,*next;
};
template
<
typename
T>
class
DLink{
private
:
node<T> *head;
public
:
DLink()
{
head=
new
node<T>;
head->next=head->prior=head;
}
~DLink()
{
clearLink();
delete
head;
}
void
clearLink()
{
node<T> *p=head->next;
while
(p!=head)
{
p=p->next;
delete
p->prior;
}
head->prior=head->next=head;
}
bool
empty()
{
return
head->next==head;
}
int
linkLength()
{
node<T> *p=head->next;
int
i=0;
while
(p!=head)
{
i++;
p=p->next;
}
return
i;
}
bool
getElem(
int
i,T &e)
{
if
(i<1 || i>linkLength())
return
false
;
node <T> *p=head->next;
int
j=1;
while
(j<i)
{
j++;
p=p->next;
}
e=p->data;
return
true
;
}
bool
Insert(
int
i,T e)
{
if
(i<1 || i> linkLength()+1)
return
false
;
node<T> *s,*p=head;
int
j=0;
s=
new
node<T>;
s->data=e;
while
(j<i-1)
{
j++;
p=p->next;
}
s->next=p->next;
s->prior=p;
p->next->prior=s;
p->next=s;
return
true
;
}
bool
Delete(
int
i,T &e)
{
if
(i<1 || i>linkLength())
return
false
;
node<T> *p=head->next;
int
j=0;
while
(j<i-1)
{
j++;
p=p->next;
}
p->prior->next=p->next;
p->next->prior=p->prior;
delete
p;
return
true
;
}
void
print()
{
node<T> *p=head->next;
while
(p!=head)
{
cout<<p->data<<
" "
;
p=p->next;
}
cout<<endl;
}
template
<
class
T>
friend
ostream & operator <<(ostream &,DLink<T> &);
template
<
class
T>
friend
void
MergeLink(DLink<T>&,DLink<T>&);
template
<
class
T>
friend
void
MergeLink(DLink<T>&,DLink<T>&,DLink<T>&);
};
template
<
class
T>
ostream & operator <<(ostream & out ,DLink<T> & a)
{
node<T> *p=a.head->next;
while
(p!=a.head)
{
out<<p->data<<
" "
;
p=p->next;
}
out<<endl;
return
out;
}
template
<
class
T>
void
MergeLink(DLink<T>&a,DLink<T>&b)
{
node<T>*pa,*pb,*p;
pa=a.head->next;
pb=b.head->next;
p=a.head;
while
(pa!=a.head && pb!=b.head)
{
if
(pa->data<=pb->data)
{
p->next=pa;
pa->prior=p;
p=pa;
pa=pa->next;
}
else
{
p->next=pb;
pb->prior=p;
p=pb;
pb=pb->next;
}
}
while
(pa!=a.head)
{
p->next=pa;
pa->prior=p;
p=pa;
pa=pa->next;
}
while
(pb!=b.head)
{
p->next=pb;
pb->prior=p;
p=pb;
pb=pb->next;
}
p->next=a.head;
a.head->prior=p;
b.head->next=b.head->prior=b.head;
}
template
<
class
T>
void
MergeLink(DLink<T>&a,DLink<T>&b,DLink<T>&c)
{
node<T> *pa,*pb,*p;
pa=a.head->next;
pb=b.head->next;
int
i=1,j=1,k=0;
T ai,bj;
while
(pa!=a.head && pb!=b.head)
{
a.getElem(i,ai);
b.getElem(j,bj);
if
(ai<=bj)
{
c.Insert(++k,ai);
i++;
pa=pa->next;
}
else
{
c.Insert(++k,bj);
j++;
pb=pb->next;
}
}
while
(pa!=a.head)
{
a.getElem(i++,ai);
pa=pa->next;
c.Insert(++k,ai);
}
while
(pb!=b.head)
{
b.getElem(j++,bj);
pb=pb->next;
c.Insert(++k,bj);
}
}
int
main()
{
DLink<
int
> La,Lb,Lc;
cout<<boolalpha<<La.empty()<<endl;
for
(
int
i=1;i<4;i++)
La.Insert(i,i);
for
(
int
i=1;i<4;i++)
Lb.Insert(i,i+1);
//cout<<boolalpha<<La.empty()<<endl;
//La.print();
//cout<<La<<"链表La的长度是:"<<La.linkLength()<<endl;
//cout<<Lb<<"链表的Lb长度是:"<<Lb.linkLength()<<endl;
//cout<<"链表的长度是:"<<La.linkLength()<<endl;
int
e;
for
(
int
i=1;i<5;i++)
{
La.getElem(i,e);
cout<<e<<
" "
;
}
cout<<endl;
while
(!La.empty())
{
La.Delete(La.linkLength(),e);
La.print();
}
//MergeLink(La,Lb);
MergeLink(La,Lb,Lc);
cout<<La<<
"链表La的长度是:"
<<La.linkLength()<<endl;
cout<<Lb<<
"链表的Lb长度是:"
<<Lb.linkLength()<<endl;
cout<<Lc<<
"链表的Lc长度是:"
<<Lc.linkLength()<<endl;
return
0;
}