C语言程序设计(王立柱)第八章答案 链表

简介: 只有聪明人才能看见的摘要~( ̄▽ ̄~)~

 先附一个

node.h

list.h

Josephus.c

#pragma once
//node.h
#include<stdlib.h>
typedef struct Node{
  Type data;
  struct Node* prev;
  struct Node* next;
}Node;
Node* GetNode(Type item, Node* p, Node* n);
Type GetData(Node* itr) { return itr->data; }
Node* GetPrev(Node* itr) { return itr->prev; }
Node* GetNext(Node* itr) { return itr->next; }
Node* GetNode(Type item, Node* p, Node* n) {
  Node* re;
  re = (Node*)malloc(sizeof(Node));
  re->data = item;
  re->prev = p;
  re->next = n;
  return re;
}
#pragma once
//list.h
#include"node.h"
typedef struct {
  Node* head;
  Node* tail;
  int size;
}List;
void InitList(List* l);             //准构造函数,建立空表
int Size(List* l) { return l->size; }
int Empty(List* l) { return l->size == 0; }
//读取区间边界
Node* Begin(List* l) { return l->head->next; }  //读取数据首节点指针
Node* End(List* l) { return l->tail; }      //读取链表尾节点指针
Node* Insert(List* l, Node* itr, Type item);  //定点插入到itr前面
void PushFront(List* l, Type item) { Insert(l, Begin(l), item); }//首插
void PushBack(List* l, Type item) { Insert(l, End(l), item); }//尾插
Node* Erase(List* l, Node* itr);        //删除itr指向的结点,返回后继指针
void PopFront(List* l) { Erase(l, Begin(l)); }  //首删
void PuopBack(List* l) { Erase(l, GetPrev(End(l))); }//尾删
void Clear(List* l) { while (!Empty(l))PopFront(l); }//清表
void FreeList(List* l) { Clear(l); free(l->head); free(l->tail); }//准析构函数
void InitList(List* l) {
  l->head = GetNode(0, NULL, NULL);
  l->tail = GetNode(0, 0, 0);
  l->head->next = l->tail;
  l->tail->prev = l->head;
  l->size = 0;
}
Node* Insert(List* l, Node* itr, Type item) {
  //Node* p = itr;
  itr->prev->next = GetNode(item, itr->prev, itr);
  itr->prev = itr->prev->next;
  l->size++;
  return itr->prev;
}
Node* Erase(List* l, Node* itr) {
  //Node* p = itr;
  Node* re = itr->next;
  itr->prev->next = itr->next;
  itr->next->prev = itr->prev;
  free(itr);
  l->size--;
  return re;
}
//Josephus.c
#include<stdio.h>
#include<time.h>
typedef int Type;
#include"list.h"
void OutputList(List* l);
void Josephus(int n);
int main() {
  int n;
  printf("Enter the number of people:\n");
  scanf("%d", &n);
  Josephus(n);
  return 0;
}
void OutputList(List* l) {
  Node* first = Begin(l);
  Node* last = End(l);
  for (; first != last; first = GetNext(first))
    printf("%d\t", GetData(first));
  printf("\n");
}
void Josephus(int n) {
  int counter;          //计数器
  int step;           //随机步长
  Node* first;
  Node* last;
  List Party;           //参与者
  List Loser;           //淘汰者
  List Odd;           //随机步长
  InitList(&Party);
  InitList(&Loser);
  InitList(&Odd);
  for (counter = 1; counter <= n; counter++)
    PushBack(&Party, counter);  //参赛者输入
  printf("Party:\n");       //参赛者输出
  OutputList(&Party);
  srand(time(0));
  first = Begin(&Party);      //数据首结点
  last = End(&Party);       //链表尾结点
  while (Size(&Party) > 1) 
  {
    step = 1 + rand() % 10;
    PushBack(&Odd, step);
    for (counter = 1; counter < step; counter++) {
      first = GetNext(first);     //计算步数一步步把first移到淘汰者处
      if (first == last)        //把链表尾结点转到数据首结点
        first = Begin(&Party);
    }
    PushBack(&Loser,GetData(first));        //淘汰者入Loser
    first = Erase(&Party, first);   //淘汰者淘汰
    if (first == last)        //把链表尾结点转到数据首结点
      first = Begin(&Party);
  }
  printf("Odds:\n");
  OutputList(&Odd);
  printf("Loser:\n");
  OutputList(&Loser);
  printf("Winner:\n");
  printf("%d\n", *Begin(&Party));
//用 printf 语句来打印结构体类型的变量,结果是 undefined behavior!
//什么是未定义行为,就是说发生任何状况都是可能的,这个就要看编译器的实现方式了。
//当然改成GetData(Begin(&Party))就没问题了。
  FreeList(&Party);
  FreeList(&Loser);
  FreeList(&Odd);
}

image.gif

有两个点需要注意

第一:书上的代码printf("%d\n", *Begin(&Party));这是不严谨的,应该改GetData(Begin(&Party)),虽然结果是对的,但是由于编译器的行为不同,可能出现不同的结果。我找到这个问题的答案来源于blog.csdn.net/weixin_42181686/article/details/117102972?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%A6%82%E6%9E%9Cprintf%E7%9A%84%25d%E5%8D%B4%E6%98%AF%E4%B8%80%E4%B8%AA%E7%BB%93%E6%9E%84%E6%80%8E%E4%B9%88%E5%8A%9E&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-.pc_search_download_positive&spm=1018.2226.3001.4187

第二,书上的list的插入和删除函数都新定义了一个Node* p; 来p=itr; 我觉得去掉也没区别,因此这导致了我一上午的折磨,调用GetNext()和Erase()函数时无数次first变成空指针然后访问越界程序异常终止,我人都快折磨疯了又换回书上的代码然后运行成功了,这没什么,关键是我百思不得其解又找不到答案就又改回了自己的代码,然后又每次都成功了,wtm,无话可说

题目:

选择排序和链表复制(只贴源文件,需要的头文件前面已经贴了)

#include<stdio.h>
typedef int Type;
#include"list.h"
void OutputList(const List* l);
void Selection(List* l);
void Copy(const List* lo, List* lt);
Node* MaxOfNode(Node* n);
int main() {
  int n;
  int i;
  List lo;
  List lt;
  InitList(&lo);
  InitList(&lt);
  int temp = 0;
  printf("输入链表长度n和n个整数:\n");
  scanf("%d", &n);
  for (i = 0; i < n; i++) {
    scanf("%d", &temp);
    PushBack(&lo, temp);
  }
  Selection(&lo);
  Copy(&lo, &lt);
  OutputList(&lo);
  OutputList(&lt);
  FreeList(&lo);
  FreeList(&lt);
  return 0;
}
void OutputList(const List* l) {
  Node* first = Begin(l);
  Node* last = End(l);
  for (; first != last; first = GetNext(first))
    printf("%d\t", GetData(first));
  printf("\n");
}
Node* MaxOfNode(Node* n) {
  int i;
  Node* temp = n;
  Node* max = temp;
  while (temp->next->next != 0) {//temp最多为倒数第二个数据结点
    if (GetData(max) < GetData(temp->next))
      max = temp->next;
    temp = temp->next;
  }
  return max;
}
void Selection(List* l) {
  Node* max;
  Node* temp = Begin(l);//temp是从第一个到倒数第二个数据节点的定位标
  while (temp->next->next != 0) {//temp最多为倒数第二个数据结点
    max = MaxOfNode(temp);
    if (max == temp) {
      temp = temp->next;
      PushFront(l, GetData(temp->prev));
      Erase(l, temp->prev);
    }
    else {
      PushFront(l, GetData(max));
      Erase(l, max);
    }
  }
  PushFront(l, GetData(End(l)->prev));
  Erase(l, End(l)->prev);
}
void Copy(const List* lo, List* lt) {
  Node* one = Begin(lo);
  while (one->next != 0) {
    PushBack(lt, GetData(one));
    one = one->next;
  }
}

image.gif


目录
相关文章
|
2天前
|
C语言
C语言程序设计核心详解 第四章&&第五章 选择结构程序设计&&循环结构程序设计
本章节介绍了C语言中的选择结构,包括关系表达式、逻辑表达式及其运算符的优先级,并通过示例详细解释了 `if` 语句的不同形式和 `switch` 语句的使用方法。此外,还概述了循环结构,包括 `while`、`do-while` 和 `for` 循环,并解释了 `break` 和 `continue` 控制语句的功能。最后,提供了两道例题以加深理解。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
2天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
2天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第九章 结构体与链表概要详解
本文档详细介绍了C语言中的结构体与链表。首先,讲解了结构体的定义、初始化及使用方法,并演示了如何通过不同方式定义结构体变量。接着,介绍了指向结构体的指针及其应用,包括结构体变量和结构体数组的指针操作。随后,概述了链表的概念与定义,解释了链表的基本操作如动态分配、插入和删除。最后,简述了共用体类型及其变量定义与引用方法。通过本文档,读者可以全面了解结构体与链表的基础知识及实际应用技巧。
|
2天前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。
|
C语言
C语言学生信息管理系统链表实现
C语言学生信息管理系统链表实现
172 0
C语言学生信息管理系统链表实现
史上最简单的C语言链表实现,没有之一
#include #include #include #define NR(x) (sizeof(x)/sizeof(x[0])) struct node { int data ; struct node *next ; }; void top_append_li...
986 0
|
8天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。