树和树结构

Published on:
树

树结构的定义和属性#

定义:具有相同特性的n(n>=0)个数据节点组成的一个有穷序列,节点之间存在一对多的关系。

  • 当n等于0时,为空树
  • 当n大于0时,有且仅有一个根节点,且根节点没有前驱节点
    • 除根节点外,其余节点都有且仅有一个前驱节点
    • 树中每个节点都有零个或者多个后继节点

属性(如上图):

  • 空树 节点数为0的树称为空树
  • 根节点 节点①为根节点,根节点没有前驱节点,在树中具有唯一性
  • 前驱节点 前驱节点是一个相对位置的概念,节点①是节点②③的前驱节点
  • 后继节点 后继节点是一个相对位置的概念,节点②③是节点①的后继节点

    换一种说法:节点①的后继节点是节点②,节点①的后继节点是节点③

  • 子节点 节点的后继节点称为子节点
  • 父节点 节点的前驱节点称为父节点
  • 兄弟节点 具有相同父节点的子节点互为兄弟节点
  • 子孙节点 节点的所有子树中的节点称为子孙节点
  • 祖先节点 从树根节点到达节点的路径上经过的所有节点被称为该节点的祖先节点

抽象数据类型#

1
2
3
4
5
6
7
8
9
10
11
12
13
ADT Tree
{
数据对象:
D = {ai | ai ∈ ElemType, i=1,2,…,n, n>=0 } // ElemType为类型标识符
数据关系:
R = {<ai,aj> | ai, aj∈D, i=1,2,…,n, j=1,2,…,n,其中每个元素只有一个前驱节点 ,可以有零个或多个后继节点,有且仅有一个元素(根节点)没有前驱节点 }
数据操作:
(1) treeSeq * InitTree(); //构造一个空的树t
(2) DestroyTree(treeSeq *t); //释放树t占用的内存空间
(3) Parent(treeSeq *t); //求t所指节点的双亲结点
(4) Sons(treeSeq *t); //求t所指节点的子孙节点
... ...
}

树的运算主要分为三大类:

  1. 寻找满足某种特定关系的节点,如寻找双亲节点;
  2. 插入或删除某个节点,如在树的当前节点上插入一个新节点 或删除当前节点的第i个孩子节点等;
  3. 遍历树中每个节点。

总结起来就是查找、增删、遍历

树的表示方法#

树的表示方法:树形表示法、文氏图表示法、凹入表示法、括号表示法

树的表示方法

树的遍历#

树的遍历指按照一定次序访问树中所有节点,并且每个节点仅被访问一次的过程。

三种方法实现树的遍历:

  1. 先根遍历
    若树不空,则先访问根节点,然后依次从左到右访问子节点。先根遍历上图的树:1 2 4 8 9 5 10 11 3 6 7

  2. 后根遍历
    若树不空,则先依次从左到右访问子节点,然后访问根节点。后根遍历上图的树:8 9 4 10 11 5 2 6 7 3 1

  3. 层次遍历
    若树不空,则自上而下,自左至右访问树中每个节点。层次遍历上图的树:1 2 3 4 5 6 7 8 9 10 11

树的属性2#

  • 节点的度和树的度 树中某个节点的子节点个数称为该节点的度,节点中最大的度称为树的度

  • 分支节点与叶子节点 度不为零的节点称为分支节点,度为零的节点称为叶子节点

    节点的度数也叫节点的分支数,分支数等于1的节点称为单分支节点,分支数等于2的节点称为双分支节点,以此类推

  • 路劲与路径长度 树中任意两个节点 $d_i$ 和 $d_j$ ,若存在一个节点序列 $[d_i,d_{i1},d_{i2},…,d_j]$,使得序列中除了 $d_i$ 外的任一个节点都是其在序列中前一个节点的后继节点,则称该节点序列为由 $d_i$ 到 $d_j$ 的一条路劲。路径长度等于路径序列中节点的数量减1

    节点①和⑩之间存在一个节点序列 [①②⑤⑩],序列长度为3

  • 节点的层次和数的高度 一个节点的层次为从根节点到该节点的路径长度加1,节点的最大层次为树的高度

  • 森林 n个互不相交的树的集合称为森林

    森林转树:多个树的集合(森林)加一个根节点变成树

    树转森林:一个树的集合去掉一个根节点变成森林

树的性质#

性质1 树中的节点数等于所有节点的度数和加1

证明:

  1. 已知:树中的节点数 = 根节点数 + 分支节点数(除根节点) + 叶子节点数
  2. 因为:所有节点的度数 = 叶子节点数 + 分支节点数(除根节点)
  3. 所以:树中的节点树 = 所有节点的度数 + 1

性质2 度为m的树中第i层(i>=1)上至多有 $m^{i-1}$ 个节点

证明:

  1. 当i=1时,$m^{i-1}=m^0=1$ ,表示第一层有1个节点,命题成立
  2. 假设当i=n-1时,$m^{i-1}=m^{n-2}$ 命题成立
  3. 则当i=n时,第n层的最多节点数是第n-1层的最多节点数的m倍,于是第n层的节点数为: $m^{n-2}*m=m^{n-1}$。命题成立。

性质3 高为h的m次树至多有 $\frac{m^h-1}{m-1}$ 个节点

根据性质2可以计算出m度树每一层至多节点数量 $m^{i-1}$,当每一层都达到最多节点数,那么高度为h的m度树,最多节点数:
$m^{1-1} + m^{2-1} + m^{3-1} + … + m^{h-1} = m^0 + m^1 + m2 + … + m^{h-1} $
通过等比数列前n项和公式可得结果: $\frac {m^h-1}{m-1} $

性质4 具有n个节点的m次树的最小高度h为 $\log_{m}n(m-1)+1$

证明:

  1. 设具有n个节点的m次树的高度为h
  2. 若在该树中前h-1层都是满的,即每一层的节点数都等于 $m^{i-1} (1<=i<=h-1)$ ,则该树具有最小的高度
  3. 第h层(即最后一层)的节点数可能满,也可能不满,高度h可计算如下:
  4. $\frac{m^{h-1}-1}{m-1} < n <= \frac{m^h-1}{m-1} $
    • 两边乘(m-1) : $m^{k-1}-1 < n(m-1) <= m^{k}-1$
    • 两边加1求对数 : $h-1 < \log_m(n(m-1)+1) <= h$
  5. $\log_m(n(m-1)+1) <= h < log_m(n(m-1)+1)+1$
  6. 因为h为整数,所以 $h=log_m(n(m-1)+1)$

树的存储结构#

双亲存储结构

存储结构:顺序存储结构

把树的节点按顺序存储到每一个节点,节点包含有值域和父节点的索引下标

1
2
3
4
5
6
7
8
#define MaxSize 50
typedef int ElemType;

typedef struct
{
ElemType date;
int parent;
} PTree[MaxSize];

孩子链存储结构

存储结构:链式存储结构

存储每个节点的值,并且保存所有孩子的指针
由于每个节点的孩子数量可能不一样(节点的度),所以按最大节点的度(树的度)数来设计节点的指针数量

1
2
3
4
5
6
#define MaxSize 50
typedef struct node
{
ElemType date;
struct node *sone[MaxSize];
} TSonNode;

孩子兄弟链存储结构

存储结构:链式存储结构

存储每个节点的值,并且保存第一个孩子的指针和下一个兄弟的指针

1
2
3
4
5
6
7
typedef int ElemType;
typedef struct tnode·
{
ElemType date; //节点的值
struct tnode *hp; //指向兄弟
struct tnode *vp; //指向孩子节点
}TSBNode;

RabbitMQ入门

Published on:
Tags: rabbitmq

RabbitMQ简介#

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅),可靠、安全。

RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

安装RabbitMQ#

rabbitmq 的安装参考 windows10环境下的RabbitMQ安装步骤(图文)

手动收发消息#

RabbitMQ安装好后,接着测试收发消息。

Add a user#

User

用户Tags,即是用户角色,不同角色拥有不同的权限。

Management(普通管理者):仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。

Policymaker(策略制定者):可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息。

Monitoring(监控者):可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)。

Administrator(超级管理员):可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。

用户创建成功后,退出当前用户,使用刚创建的用户登陆。

Add a new virtual host#

vhost

Vhost创建成功后,点击Vhost进入管理面板,展开 Permissions 一栏,可以看到当前用户已经在权限列表里了。
表示用户连接到RabbitMQ后,有权限向Exchange转发消息,或者消费Queue里的消息。

客户端连接到RabbitMQ,需要指定使用什么账户登陆、连接到哪一个Vhost,所以需要保证用户有权限操作指定Vhost。

如果需要为其他的用户赋予权限,则通过 Set permission 动作完成。

Add a new exchange#

exchange

Add a new queue#

queue

以上 交换机(Exchange) 和 队列(Queue) 必须同属于同一个 Vhost。

Bindings#

binding

队列必须绑定到一个交换机上,并且指定一个路由键(Routing key)。

Publish message#

sen

消息首先到达交换机,交换机根据路由键按照路由算法,把消息路由给指定的队列。
因此,测试需要从交换机发送消息。

使用队列里的发送消息功能,消息会被以队列名为路由键发送到相同虚拟机下的默认的交换机。

默认交换机隐性的绑定到每一个队列,默认交换机接收到消息后,使用队列名作为路由键,发送到队列,所以消息实际上又从交换机转发到队列。

Get messages#

rec

消息应答模式(Ack Mode)可以选择 Ack message 或者Nack message。
Ack(acknowledgement)模式表示消费者接收到消息,rabbitMQ可以删除消息了,Nack(not acknowledgement)模式表示消费者接收到消息,但是rabbitMQ依然缓存消息。

系统架构#

RabbitMQ系统最核心的组件是交换机(Exchange)和队列(Queue),下图是系统简单的示意图。交换机和队列是在rabbitmq server(又叫做broker)端,生产者(producer)和消费者(consumer)在应用端。

RabbitMQ系统可以创建多个虚拟主机(Virtual host),提供给不同的用户使用。用户在自己的虚拟主机里创建交换机和队列,实现消息队列功能。因此虚拟主机在逻辑上隔离了不同用户的消息。

架构

生产者&消费者#

producer指的是消息生产者,consumer消息的消费者。

队列#

消息队列,提供FIFO的处理机制,交换机转发消息入队,消费者消费消息出队。
队列存储消息的地方,因此队列具有缓存消息的能力,缓存方式有:

  • 持久化(Durable): 消息会在服务端本地硬盘存储一份,防止系统崩溃,数据丢失
  • 临时(Transient): 数据在系统重启之后就会丢失

交换机#

交换机提供消息的路由策略。
消息由生产者提供,生产者通过信道把消息转发给交换机,交换机具有与队列一样的缓存消息的功能。
一个交换机可与多个队列绑定,交换机根据路由键按照路由算法,把消息路由给不同的队列。
交换机的路由策略与交换机的类型和路由键有关,交换机有4种类型:Direct、fanout、topic、headers。

  • Direct: 交换机将消息转发给与路由缉拿完成一样的队列
  • fanout: 忽略路由键,交换机把消息转发给所有绑定的队列
  • topic: 以路由键为模式,交换机将消息转发给与模式匹配的队列
  • headers: 消息体的header匹配

绑定#

把交换机和队列绑定在一起,并且付带一个路由键。一个交换机可以绑定多个队列。

交换机除了可以绑定队列,还可以绑定交换机,同样需要付带一个路由键

虚拟主机#

一个RabbitMQ系统可以有多个虚拟主机,每个虚拟主机管理各自的交换机、队列和绑定。
虚拟主机在逻辑上分离数据,使得不同应用的数据安全运行,互不干扰。
生产者和消费者连接RabbitMQ系统需要指定一个虚拟主机。

通信过程#

  • 消费者与RabbitMQ系统取得连接,并且订阅了一个指定队列
  • 生产者与RabbitMQ系统取得连接
  • 生产者通过信道(Channel)把消息(message)发送给交换机,并且提供一个路由键(Routing key)
  • 交换机根据路由键按照路由算法,把消息路由给指定的队列
  • 队列收到消息后,会把消息发送给消费者
  • 消费者收到消息后,发送ACK命令给队列,表示确认
  • 队列收到ACK命令后,从队列中删除此条消息

参考#

[1] Rabbitmq基本原理

[2] AMQP协议学习

JS Date日期助手函数

Published on:
Tags: javascript

时间参数 sdate 可为时间格式字符串或者整型时间戳,例如:'2020-07-01 12:12:12'1656648732000

以下

1
2
>   表示输入
<· 表示输出

初始化#

1
2
3
4
5
6
7
8
9
10
var initDate = function(sdate)
{
if(
sdate == ''
|| typeof (sdate) == 'undefined'
)
return new Date();
else
return new Date(sdate);
}

获取年份#

1
2
3
4
var getYear = function(sdate)
{
return initDate(sdate).getFullYear();
}
1
2
>  getYear('2020-07-01');
<· 2020

获取月份#

1
2
3
4
var getMonth = function(sdate)
{
return initDate(sdate).getMonth() + 1;
}
1
2
>  getMonth('2020-07-01');
<· 7

获取日期#

1
2
3
4
var getDate = function(sdate)
{
return initDate(sdate).getDate();
}
1
2
>  getDate('2020-07-01');
<· 1

获取年月日#

1
2
3
4
5
6
7
8
9
var getDates = function(sdate)
{
let curdate = initDate(sdate);
let month = curdate.getMonth() + 1;
let date = curdate.getDate();
month = month < 10?'0'+month:month;
date = date < 10?'0'+date:date;
return curdate.getFullYear()+'-'+month+'-'+date;
}
1
2
>  getDates('2020-07-01');
<· "2020-07-01"

获取小时#

1
2
3
4
var getHours = function(sdate)
{
return initDate(sdate).getHours();
}
1
2
>  getHours('2020-07-01 12:12:12');
<· 12

获取分钟#

1
2
3
4
var getMinutes = function(sdate)
{
return initDate(sdate).getMinutes();
}
1
2
>  getMinutes('2020-07-01 12:12:12');
<· 12

获取秒钟#

1
2
3
4
var getSeconds = function(sdate)
{
return initDate(sdate).getSeconds();
}
1
2
>  getSeconds('2020-07-01 12:12:12');
<· 12

获取毫秒#

1
2
3
4
var getMilliseconds = function(sdate)
{
return initDate(sdate).getMilliseconds();
}
1
2
>  getMilliseconds('2020-07-01 12:12:12');
<· 0

获取时分秒#

1
2
3
4
5
6
7
8
9
10
11
var getTime = function(sdate)
{
let curdate = initDate(sdate);
let hours = curdate.getHours();
let minutes = curdate.getMinutes();
let seconds = curdate.getSeconds();
hours = hours < 10?'0'+hours:hours;
minutes = minutes < 10?'0'+minutes:minutes;
seconds = seconds < 10?'0'+seconds:seconds;
return hours+':'+minutes+':'+seconds;
}
1
2
>  getTime('2020-07-01 12:12:12');
<· "12:12:12"

获取年月日时分秒#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var getDateTime = function(sdate)
{
let curdate = initDate(sdate);
let month = curdate.getMonth() + 1;
let date = curdate.getDate();
let hours = curdate.getHours();
let minutes = curdate.getMinutes();
let seconds = curdate.getSeconds();
month = month < 10?'0'+month:month;
date = date < 10?'0'+date:date;
hours = hours < 10?'0'+hours:hours;
minutes = minutes < 10?'0'+minutes:minutes;
seconds = seconds < 10?'0'+seconds:seconds;
return curdate.getFullYear()+'-'+month+'-'+date+' '+hours+':'+minutes+':'+seconds;
}
1
2
3
4
>  getDateTime('2020-07-01 12:12:12');
<· "2020-07-01 12:12:12"
> getDateTime(1593576732000);
<· "2020-07-01 12:12:12"

获取两个日期相差的天数#

1
2
3
4
var getDiffDate = function(sdate,edate)
{
return (getTimeStamp(getDates(edate)) - getTimeStamp(getDates(sdate)))/(24*60*60*1000);
}
1
2
>  getDiffDate('2020-06-01','2020-07-01');
<· 30

获取星期#

1
2
3
4
5
6
var getDay = function(sdate)
{
let day = initDate(sdate).getDay();
if(day == 0) return 7;
else return day;
}
1
2
>  getDay('2020-07-01 12:12:12');
<· 3

获取周序号#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 第几周 week of year
var getWof = function(sdate,start)
{
if(start < 1 || start > 7) return false;
let end = start - 1; // 一周的最后一天是星期几
if(end == 0) end = 7;
let firstDate = getDates(getYear(sdate)+'');
let firstDay = getDay(firstDate); // 当年第一天是星期几
if(end < firstDay) end += 7;
let firstWeekDays = end - firstDay + 1; // 当年的第一周天数
let date = getDates(sdate);
let diffDate = getDiffDate(firstDate,date) + 1;
if(diffDate <= firstWeekDays) return 1;
let subDate = diffDate - firstWeekDays;
let weeks = Math.floor(subDate/7);
if(subDate%7) weeks++;
return weeks+1;
}
1
2
>  getWof('2020-07-01 12:12:12',6);
<· 27

获取时间戳#

1
2
3
4
var getTimeStamp = function(sdate)
{
return initDate(sdate).getTime();
}
1
2
>  getTimeStamp('2020-07-01 12:12:12');
<· 1593576732000

加/减 年份#

1
2
3
4
5
var setYear = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setFullYear(curdate.getFullYear() + nums);
}
1
2
3
4
>  setYear('2020-07-01 12:12:12',2);
<· 1656648732000
> getDateTime(1656648732000);
<· "2022-07-01 12:12:12"

加/减 月份#

1
2
3
4
5
var setMonth = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setMonth(curdate.getMonth() + nums);
}
1
2
3
4
>  setMonth('2020-07-01 12:12:12',2);
<· 1598933532000
> getDateTime(1598933532000);
<· "2020-09-01 12:12:12"

加/减 日期#

1
2
3
4
5
var setDate = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setDate(curdate.getDate() + nums);
}
1
2
3
4
>  setDate('2020-07-01 12:12:12',2);
<· 1593749532000
> getDateTime(1593749532000);
<· "2020-07-03 12:12:12"

加/减 小时#

1
2
3
4
5
var setHours = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setHours(curdate.getHours() + nums);
}
1
2
3
4
>  setHours('2020-07-01 12:12:12',2);
<· 1593583932000
> getDateTime(1593583932000);
<· "2020-07-01 14:12:12"

加/减 分钟#

1
2
3
4
5
var setMinutes = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setMinutes(curdate.getMinutes() + nums);
}
1
2
3
4
>  setMinutes('2020-07-01 12:12:12',2);
<· 1593576852000
> getDateTime(1593576852000);
<· "2020-07-01 12:14:12"

加/减 秒钟#

1
2
3
4
5
var setSeconds = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setSeconds(curdate.getSeconds() + nums);
}
1
2
3
4
>  setSeconds('2020-07-01 12:12:12',2);
<· 1593576734000
> getDateTime(1593576734000);
<· "2020-07-01 12:12:14"

是否闰年#

1
2
3
4
5
6
7
8
var isLeapYear = function(sdate)
{
let year = getYear(sdate);
if(year%100)
return year%4?0:1; // 普通年
else
return year%400?0:1; // 世纪年
}
1
2
>  isLeapYear('2020-07-02')
<· 1

当月有几天#

1
2
3
4
5
6
7
8
var getDaysInMonth = function(sdate)
{
let daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
let month = getMonth(sdate);
let days = daysInMonth[month-1];
if(month == 2 && isLeapYear(sdate)) days++; // 闰年2月
return days;
}
1
2
>  getDaysInMonth('2020-02-02')
<· 29

JS代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//  date-helper.js

// 实例化,参数为时间格式字符串或者整型时间戳
var initDate = function(sdate)
{
if(
sdate == ''
|| typeof (sdate) == 'undefined'
)
return new Date();
else
return new Date(sdate);
}
// 年
var getYear = function(sdate)
{
return initDate(sdate).getFullYear();
}
// 月
var getMonth = function(sdate)
{
return initDate(sdate).getMonth() + 1;
}
// 日
var getDate = function(sdate)
{
return initDate(sdate).getDate();
}
// 日期
var getDates = function(sdate)
{
let curdate = initDate(sdate);
let month = curdate.getMonth() + 1;
let date = curdate.getDate();
month = month < 10?'0'+month:month;
date = date < 10?'0'+date:date;
return curdate.getFullYear()+'-'+month+'-'+date;
}
// 时
var getHours = function(sdate)
{
return initDate(sdate).getHours();
}
// 分
var getMinutes = function(sdate)
{
return initDate(sdate).getMinutes();
}
// 秒
var getSeconds = function(sdate)
{
return initDate(sdate).getSeconds();
}
// 毫秒
var getMilliseconds = function(sdate)
{
return initDate(sdate).getMilliseconds();
}
// 时间
var getTime = function(sdate)
{
let curdate = initDate(sdate);
let hours = curdate.getHours();
let minutes = curdate.getMinutes();
let seconds = curdate.getSeconds();
hours = hours < 10?'0'+hours:hours;
minutes = minutes < 10?'0'+minutes:minutes;
seconds = seconds < 10?'0'+seconds:seconds;
return hours+':'+minutes+':'+seconds;
}
// 日期 时间
var getDateTime = function(sdate)
{
let curdate = initDate(sdate);
let month = curdate.getMonth() + 1;
let date = curdate.getDate();
let hours = curdate.getHours();
let minutes = curdate.getMinutes();
let seconds = curdate.getSeconds();
month = month < 10?'0'+month:month;
date = date < 10?'0'+date:date;
hours = hours < 10?'0'+hours:hours;
minutes = minutes < 10?'0'+minutes:minutes;
seconds = seconds < 10?'0'+seconds:seconds;
return curdate.getFullYear()+'-'+month+'-'+date+' '+hours+':'+minutes+':'+seconds;
}
// 两个日期相差的天数
var getDiffDate = function(sdate,edate)
{
return (getTimeStamp(getDates(edate)) - getTimeStamp(getDates(sdate)))/(24*60*60*1000);
}
// 星期
var getDay = function(sdate)
{
let day = initDate(sdate).getDay();
if(day == 0) return 7;
else return day;
}
// 第几周 week of year
var getWof = function(sdate,start)
{
if(start < 1 || start > 7) return false;
let end = start - 1; // 一周的最后一天是星期几
if(end == 0) end = 7;
let firstDate = getDates(getYear(sdate)+'');
let firstDay = getDay(firstDate); // 当年第一天是星期几
if(end < firstDay) end += 7;
let firstWeekDays = end - firstDay + 1; // 当年的第一周天数
let date = getDates(sdate);
let diffDate = getDiffDate(firstDate,date) + 1;
if(diffDate <= firstWeekDays) return 1;
let subDate = diffDate - firstWeekDays;
let weeks = Math.floor(subDate/7);
if(subDate%7) weeks++;
return weeks+1;
}
// 时间戳(毫秒)
var getTimeStamp = function(sdate)
{
return initDate(sdate).getTime();
}
// 加/减 年份
var setYear = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setFullYear(curdate.getFullYear() + nums);
}
// 加/减 月份
var setMonth = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setMonth(curdate.getMonth() + nums);
}
// 加/减 日期
var setDate = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setDate(curdate.getDate() + nums);
}
// 加/减 小时
var setHours = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setHours(curdate.getHours() + nums);
}

// 加/减 分钟
var setMinutes = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setMinutes(curdate.getMinutes() + nums);
}

// 加/减 秒钟
var setSeconds = function(sdate,nums)
{
let curdate = initDate(sdate);
return curdate.setSeconds(curdate.getSeconds() + nums);
}

// 是否闰年
var isLeapYear = function(sdate)
{
let year = getYear(sdate);
if(year%100)
return year%4?0:1; // 普通年
else
return year%400?0:1; // 世纪年
}

// 求当月有几天
var getDaysInMonth = function(sdate)
{
let daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
let month = getMonth(sdate);
let days = daysInMonth[month-1];
if(month == 2 && isLeapYear(sdate)) days++; // 闰年2月
return days;
}

leetcode 209m 长度最小的子数组

Published on:
Tags: leetcode

题目描述#

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的连续子数组,返回 0。

 
示例:

1
2
3
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的连续子数组。

数组元素 和 s均为正整数,即均大于0

算法#

  1. 定义两个数组下标:i j,遍历数组
  2. 外层循环i表示子数组的起点下标,内层循环j从位置i开始,逐个累加数组元素,同时计算数组元素个数tmplength,直到累加和大于等于s 或者 j大于等于元素个数numsSize 时停止
  3. 取得最小的元素个数

当 $i=0,j=numsSize-1,sum<s$ 时,即是:整个数组的和小于s,直接返回0

类似的,
当 $i>0,j=numsSize-1,sum<s$ 时,即是:从i到j的累加和小于s,表示i后面的元素累加和都小于s,不存在符合条件的子数组,直接跳出循环

C代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>

int minSubArrayLen(int s, int* nums, int numsSize);

int main()
{
int s = 3,numsSize = 2;
int nums[2] = {1,1};
int length = minSubArrayLen( s, &nums[0], numsSize);
printf("长度最小的子数组长度length=%d",length);
return 0;
}

int minSubArrayLen(int s, int* nums, int numsSize)
{
int length = numsSize;

for(int i=0;i<numsSize;i++)
{
int sum = 0,tmpLength = 0,j;

for(j=i;j<numsSize && sum<s;j++)
{
sum += nums[j];
tmpLength++;
}

if(sum < s)
{
if(i == 0) return 0;
else break;
}

if(tmpLength < length) length = tmpLength;
printf("i=%d,j=%d,tmpLength=%d,sum=%d\n",i,j,tmpLength,sum);
}

return length;
}

时间复杂度#

算法的时间复杂度为 $O(n)$

串的模式匹配(Brute-Force算法)

Published on:

算法介绍#

Brute-Force算法也称为简单匹配算法。B-F算法使用暴力枚举的方式,从目标串S中查找与模式串T一样的子串,若存在,则返回子串的开始位置,若不存在则返回-1.

  • 目标串:$S = s_1s_2s_3…s_n$
  • 模式串:$T = t_1t_2t_3…t_n$
  • 模式匹配:查找与模式串一样的子串的过程称为模式匹配
  • 目标串长度 $sLength$,模式串长度 $tLength$

算法匹配过程#

  1. 首先模式串T的长度必须小于等于目标串
  2. 模式匹配过程
  • 从目标串 $S$ 的第 $i$ 个位置开始寻找与模式串 $T$ 一样的子串 $( 0 <= i <= sLength-tLength)$
  • 遍历目标串 $S$ ,从 $i$ 到 $i+tLength$ 之间的字符,与模式串逐个比较
    • 若出现任意一个字符不相同,则跳出遍历,$i$ 向前移动一位,重新遍历目标串 $S$
    • 若每一个字符都相同,则返回 $i$

C代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>

int bf(char *s,char *t);

int main()
{
char s[] = "[Done] exited with code=0 in 0.454 seconds";
char t[] = "Done";
int index = bf(&s[0],&t[0]);
printf("index=%d\n",index);
return 0;
}

int StrLength(char *s)
{
int i = 0;
while(*(s+i) != '\0')
i++;

return i;
}

// Brute-Force算法
int bf(char *s,char *t)
{
int sLength = StrLength(s);
int tLength = StrLength(t);
if(sLength < tLength) return -1;
int si = 0,ti = 0;
while (si+tLength <= sLength)
{
int flag = 1;
for(ti=0;ti<tLength;ti++)
if(*(s+si+ti) != *(t+ti))
{
flag = 0;
break;
}

if(flag)
return si;
else
{
ti = 0;
si++;
}
}

return -1;
}

时间复杂度#

情况 时间复杂度
最好情况: $O(tLength)$
最坏情况: $O(tLength*sLength)$

leetcode 125e 验证回文串

Published on:
Tags: leetcode

题目描述#

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明: 本题中,我们将空字符串定义为有效的回文串。

示例 1:

1
2
输入: "A man, a plan, a canal: Panama"
输出: true

示例 2:

1
2
输入: "race a car"
输出: false

算法#

  1. 去除非字母、数字字符,大写字母转成小写字母,求出字符总长度
  2. 构造字符数组
  3. 验证

C代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0
typedef int bool;


typedef struct node
{
char data;
struct node *next;
} strLink;

strLink * StrAssign(char *s);
bool isPalindrome(char * s);
void DispStr(strLink * s);
bool isPalindrome(char * s);
char GetChar(char c);
void StrCopy(strLink *str,int length,char *s);
void DispArr(char *arr,int length);

int main()
{
char s[] = "";
if(isPalindrome(&s[0]))
{
printf("true\t");
}else
{
printf("false\t");
}

return 0;
}

char GetChar(char c)
{
char res='\0';
if((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') )
res = c;

if(c >= 'A' && c <= 'Z')
res = c + 32;

return res;
}

int StrLength(strLink *s)
{
strLink * tmp = s->next;
int i = 0;
while(tmp != NULL)
{
i++;
tmp = tmp->next;
}
return i;
}

strLink * StrAssign(char *s)
{
strLink *tmp,*r,*p;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;
r = tmp;
int i = 0;
while(s[i] != '\0')
{
char res;
if((res = GetChar(s[i])) == '\0')
{
i++;
continue;
}

p = (strLink *)malloc(sizeof(strLink));
p->data = res;
r->next = p;
r = p;
i++;
}
r->next = NULL;
return tmp;
}

void DispStr(strLink *s)
{
strLink *tmp = s->next;
while(tmp != NULL)
{
printf("%c",tmp->data);
tmp = tmp->next;
}
printf("\n");
}
void DispArr(char *arr,int length)
{
for(int i=0;i<length;i++)
{
printf("%c\t",*(arr+i));
}
printf("\n");
}

void StrCopy(strLink *str,int length,char *s)
{
int i = 0;
strLink * tmp = str->next;
while(tmp != NULL)
{
*(s+i) = tmp->data;
i++;
tmp = tmp->next;
}
}

bool isPalindrome(char * s)
{
// 求出长度
strLink *str;
str = StrAssign(s);
// DispStr(str);
int length = StrLength(str);
if(length <= 1) return true;
char strArr[length];

// 构造字符数组
StrCopy(str,length,&strArr[0]);
// DispArr(&strArr[0],length);

// 验证
int i=0,j=length-1;
while(i < j)
{
if(strArr[i] != strArr[j])
return false;
i++;
j--;
}
return true;
}

时间复杂度#

算法的时间复杂度为 $O(n)$

串结构——顺序串、链串

Published on:

串结构的定义和属性#

“串”即是我们平常讨论的字符串,串是由零个或多个字符(只能是字符)组成的有穷序列。

通常记为 $S=”a_1a_2a_3…a_n”$,每个 $a_i$ 代表一个字符,其中 $i$ 表示字符在串中的位置$(1<=i<=n)$。

串结构的几个属性:

  • 长度:串中的字符个数
  • 空串:长度为0的串称为空串
  • 子串:在一个串中任意个连续字符组成的子序列(含空串),称为该串的子串
  • 真子串:不包含自身的所有子串
  • 串相等:长度相等,每个字符的位置也相等的两个串

一个长度等于n的串

真子串的数量:$\frac{n(n+1)}{2}$

子串的数量 = 真子串的数量 + 1 = $\frac{n(n+1)}{2} + 1$

例如 串abcd,子串的组合情况和数量如下:

串长度 数量 子串
0 1 空串
1 4 a,b,c,d
2 3 ab,bc,cd
3 2 abc,bcd
4 1 abcd
total 11

串结构的抽象数据类型#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ADT String
{
数据对象:
D = {ai | ai∈ElemType, i=1,2,…,n, n≧0 } //ElemType为类型标识符
数据关系:
R = {<ai, ai+1> | ai, ai+1∈D, i=1,3,…,n-1 }
数据操作:
(1) strSeq * InitStr(); // 初始化串
(2) void StrAssign(strSeq *s,char cstr[]); // 字符串常量cstr赋给串s
(3) void DispStr(strSeq *s); // 输出串
(4) strSeq * InsStr(strSeq *s,int i,strSeq *s1); // 串插入
(5) strSeq * StrCopy(strSeq *s); // 复制串s,返回新串
(6) Bool StrEqual(strSeq *s,strSeq *t); // 串相等
(7) int StrLength(strSeq *s); // 求串长
(8) strSeq * Concat(strSeq *s,strSeq *t); // 串连接
(9) strSeq * SubStr(strSeq *s,int i,int j); // 求子串
(10) strSeq * DelStr(strSeq *s,int i,int j) ; // 串删除
(11) strSeq * RepStr(strSeq *s,int i,int j,strSeq *t); // 串替换
}

虽然串是一种特殊的线性表,与线性表具有类似的逻辑结构。
但是从抽象数据类型的抽象方法可以看出来,串和线性表的操作有较大的区别。

串通常以整体作为操作的对象,可以从作为参数的输入和作为结果的返回看出。
而线性表通常以单个元素作为操作的对象。

顺序串#

定义顺序串的存储结构:

1
2
3
4
5
#define MaxSize 100    
typedef struct
{ char data[MaxSize];
int length;
} strSeq;

字符变量cstr赋值给串方法 StrAssign 和 串复制方法 StrCopy 比较类似,区别是遍历字符变量cstr时,是否以 '\0' 结束。

方法 InsStr, SubStr, DelStr, RepStr 中有参数 $i$ ,表示逻辑序号,需要转换成物理序号,避免发生越界错误。

顺序串代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include <stdio.h>
#include <stdlib.h>

#define MaxSize 100
#define true 1
#define false 0

typedef int Bool;
typedef struct
{ char data[MaxSize];
int length;
} strSeq;

strSeq * InitStr(); // 初始化串
void StrAssign(strSeq *s,char cstr[]); // 字符串常量cstr赋给串s
void DispStr(strSeq *s); // 输出串
strSeq * InsStr(strSeq *s,int i,strSeq *s1); // 串插入
strSeq * StrCopy(strSeq *s); // 复制串s,返回新串
Bool StrEqual(strSeq *s,strSeq *t); // 串相等
int StrLength(strSeq *s); // 求串长
strSeq * Concat(strSeq *s,strSeq *t); // 串连接
strSeq * SubStr(strSeq *s,int i,int j); // 求子串
strSeq * DelStr(strSeq *s,int i,int j) ; // 串删去
strSeq * RepStr(strSeq *s,int i,int j,strSeq *t); // 串替换


int main()
{
strSeq *s,*s1,*s2,*s3,*s4,*s5;
printf("串的基本运算如下:\n");
printf(" (0)初始化串s:\n");
s = InitStr();
char cstr[] = "abcdefghijklmn";
printf(" (1)字符串 %s 赋值给s\n",cstr);
StrAssign(s,cstr);
printf(" (2)输出串s:");
DispStr(s);
printf(" (0)初始化串s1:\n");
s1 = InitStr();
StrAssign(s1,"123");
printf(" (2)输出串s1:");
DispStr(s1);
printf(" (3)串s的长度:%d\n",StrLength(s));
printf(" (4)在串s的第9个字符位置插入串s1而产生串s2\n");
s2 = InsStr(s,9,s1);
printf(" (5)输出串s2:");
DispStr(s2);
printf(" (6)删除串s第2个字符开始的5个字符而产生串s2\n");
s2 = DelStr(s,2,5);
printf(" (7)输出串s2:");
DispStr(s2);
printf(" (8)将串s第2个字符开始的5个字符替换成串s1而产生串s2\n");
s2 = RepStr(s,2,5,s1);
printf(" (9)输出串s2:");
DispStr(s2);
printf(" (10)提取串s的第2个字符开始的10个字符而产生串s3\n");
s3 = SubStr(s,2,10);
printf(" (11)输出串s3:");
DispStr(s3);
printf(" (12)将串s1和串s2连接起来而产生串s4\n");
s4 = Concat(s,s1);
printf(" (13)输出串s4:");
DispStr(s4);
printf(" (14)复制串s返回s5\n");
s5 = StrCopy(s);
printf(" (15)输出串s5:");
DispStr(s5);
printf(" (16)比较串s和s5\n");
if(StrEqual(s,s5))
printf(" (17)串s和s5相等");
else
printf(" (17)串s和s5不相等");

return 0;
}

strSeq * InitStr()
{
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;
return tmp;
}

void StrAssign(strSeq *s,char cstr[])
{
int i = 0;
while (cstr[i] != '\0')
{
s->data[i] = cstr[i];
i++;
}
s->length = i;
}

strSeq * InsStr(strSeq *s,int i,strSeq *s1)
{
int j,k;
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;
i--;
if(i < 0 || i > s->length)
return tmp;
if(s->length + s1->length > MaxSize)
return tmp;

for ( j = 0; j < i; j++)
tmp->data[j] = s->data[j];
for ( k = 0; k < s1->length; k++)
tmp->data[j+k] = s1->data[k];

for ( j = i; j < s->length; j++)
tmp->data[j+s1->length] = s->data[j];

tmp->length = s->length + s1->length;
return tmp;
}

void DispStr(strSeq *s)
{
for (int i = 0; i < s->length; i++)
{
printf("%c ",s->data[i]);
}
printf("\n");
}


strSeq * Concat(strSeq *s,strSeq *t)
{
int k,l;
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;

for(k=0;k<s->length;k++)
tmp->data[k] = s->data[k];

for(k=0;k<t->length;k++)
tmp->data[s->length+k] = t->data[k];

tmp->length = s->length + t->length;
return tmp;
}


int StrLength(strSeq *s)
{
return s->length;
}

strSeq * SubStr(strSeq *s,int i,int j)
{
int k,l;
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;

if(i+j-1 > s->length)
return tmp;
i--;
for(k=i,l=0;l<j;l++,k++)
tmp->data[l] = s->data[k];

tmp->length = j;
return tmp;
}

strSeq * RepStr(strSeq *s,int i,int j,strSeq *t)
{
int k,l;
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;

if(i+j-1 > s->length)
return tmp;
i--;
for(k=0,l=0;k<i;k++,l++)
tmp->data[l] = s->data[k];

for(k=0;k<t->length;k++,l++)
tmp->data[l] = t->data[k];

for(k=i+j;k<s->length;k++,l++)
tmp->data[l] = s->data[k];

tmp->length = s->length-j+t->length;
return tmp;
}

strSeq * DelStr(strSeq *s,int i,int j)
{
int k,l;
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;
// 元素数量不够,返回空
if (i+j-1 > s->length)
return tmp;
i--;
for ( k = 0,l = 0; k < i; k++,l++)
tmp->data[l] = s->data[k];

for ( k = i+j; k < s->length; k++,l++)
tmp->data[l] = s->data[k];

tmp->length = s->length-j;
return tmp;
}

// 复制串s,返回新串
strSeq *StrCopy(strSeq *s)
{
strSeq *tmp = (strSeq *)malloc(sizeof(strSeq));
tmp->length = 0;
for(int i=0;i<s->length;i++)
tmp->data[i] = s->data[i];

tmp->length = s->length;
return tmp;
}

Bool StrEqual(strSeq *s,strSeq *t)
{
if(s->length != t->length)
return false;

for(int i=0;i<s->length;i++)
if(s->data[i] != t->data[i])
return false;

return true;
}

链串#

链串的存储结构定义为:

1
2
3
4
5
typedef struct node
{
char data;
struct node *next;
} strLink;

链串代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0

typedef int Bool;
typedef struct node
{
char data;
struct node *next;
} strLink;

strLink * InitStr(); //初始化串
void StrAssign(strLink *s,char cstr[]); //字符串常量cstr赋给串s
void DispStr(strLink *s); //输出串
int StrLength(strLink *s); //求串长
strLink * InsStr(strLink *s,int i,strLink *t); //串插入
strLink * DelStr(strLink *s,int i,int j); //串删去
strLink * RepStr(strLink *s,int i,int j,strLink *t); //串替换
strLink * SubStr(strLink *s,int i,int j); //求子串
strLink * Concat(strLink *s,strLink *t); //串连接
strLink * StrCopy(strLink *s); //复制串s,返回新串
Bool StrEqual(strLink *s,strLink *t); //判串相等

int main()
{
strLink *s,*s1,*s2,*s3,*s4,*s5;
printf("链串的基本运算如下:\n");
printf(" (1)建立串s和串s1\n");
s = InitStr();
StrAssign(s,"abcdefghijklmn");
printf(" (2)输出串s:");
DispStr(s);
s1 = InitStr();
StrAssign(s1,"123");
printf(" (2)输出串s1:");
DispStr(s1);
printf(" (3)串s的长度:%d\n",StrLength(s));
printf(" (4)在串s的第9个字符位置插入串s1而产生串s2\n");
s2 = InsStr(s,9,s1);
printf(" (5)输出串s2:");
DispStr(s2);
printf(" (6)删除串s第2个字符开始的5个字符而产生串s2\n");
s2=DelStr(s,2,5);
printf(" (7)输出串s2:");
DispStr(s2);
printf(" (8)将串s第2个字符开始的5个字符替换成串s1而产生串s2\n");
s2 = RepStr(s,2,5,s1);
printf(" (9)输出串s2:");
DispStr(s2);
printf(" (10)提取串s的第2个字符开始的10个字符而产生串s3\n");
s3 = SubStr(s,2,10);
printf(" (11)输出串s3:");
DispStr(s3);
printf(" (12)将串s和串s1连接起来而产生串s4\n");
s4 = Concat(s,s1);
printf(" (13)输出串s4:");
DispStr(s4);

printf(" (14)复制串s返回s5\n");
s5 = StrCopy(s);
printf(" (15)输出串s5:");
DispStr(s5);

printf(" (16)比较串s和s5\n");
if(StrEqual(s,s5))
printf(" (17)串s和s5相等");
else
printf(" (17)串s和s5不相等");
return 0;
}

strLink * InitStr()
{
strLink *tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;
return tmp;
}

void StrAssign(strLink *s,char cstr[])
{
int i = 0;
strLink *tmp,*p;
p = s;
while(cstr[i] != '\0')
{
tmp = (strLink *)malloc(sizeof(strLink));
tmp->data = cstr[i];
p->next = tmp;
p = tmp;
i++;
}
p->next = NULL;
}

void DispStr(strLink *s)
{
strLink *tmp = s->next;
while (tmp != NULL)
{
printf("%c ",tmp->data);
tmp = tmp->next;
}
printf("\n");
}

int StrLength(strLink *s)
{
strLink *tmp = s->next;
int i = 0;
while (tmp != NULL)
{
i++;
tmp = tmp->next;
}
return i;
}

strLink * InsStr(strLink *s,int i,strLink *t)
{
strLink *tmp,*p1=s->next,*p2=t->next,*q,*r;
int j;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;
i--; // 逻辑顺序转物理顺序
if(i < 0 || i > StrLength(s))
return tmp;

r = tmp;
for ( j = 0; j < i; j++)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
}

while(p2 != NULL)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p2->data;
r->next = q;
r = q;
p2 = p2->next;
}
for ( j = i; j < StrLength(s); j++)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
}
r->next = NULL;

return tmp;
}

strLink *DelStr(strLink *s,int i,int j)
{
strLink *tmp,*r,*q,*p = s->next;
int k=0;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;

if(i+j-1 > StrLength(s))
return tmp;
r = tmp;
i--;
while (k < i)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p->data;
r->next = q;
r = q;
p = p->next;
k++;
}

while (k < i+j){
p = p->next;
k++;
}

while ( k < StrLength(s))
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p->data;
r->next = q;
r = q;
p = p->next;
k++;
}
r->next = NULL;
return tmp;
}


strLink *RepStr(strLink *s,int i,int j,strLink *t)
{
strLink *tmp,*r,*q,*p1=s->next,*p2=t->next;
int k = 0;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;

if(i+j-1 > StrLength(s))
return tmp;
r = tmp;
i--;
while (k < i)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
k++;
}

while (p2 != NULL)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p2->data;
r->next = q;
r = q;
p2 = p2->next;
}

while (k < i+j)
{
p1 = p1->next;
k++;
}

while ( k < StrLength(s))
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
k++;
}
r->next = NULL;

return tmp;
}

strLink * SubStr(strLink *s,int i,int j)
{
int k = 0;
strLink *tmp,*p = s->next,*r,*q;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;

if(i-1+j > StrLength(s))
return tmp;
r = tmp;
i--;
while (k < i)
{
p = p->next;
k++;
}

while (k < i+j)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p->data;
r->next = q;
r = q;
p = p->next;
k++;
}
r->next = NULL;
return tmp;
}

strLink * Concat(strLink *s,strLink *t)
{
strLink *tmp,*r,*q,*p1 =s->next,*p2=t->next;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;

r = tmp;
while (p1 != NULL)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
}

while (p2 != NULL)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p2->data;
r->next = q;
r = q;
p2 = p2->next;
}
r->next = NULL;
return tmp;
}

// 复制串s,返回新串
strLink * StrCopy(strLink *s)
{
strLink *tmp,*r,*q,*p1=s->next;
tmp = (strLink *)malloc(sizeof(strLink));
tmp->next = NULL;

r = tmp;
while (p1 != NULL)
{
q = (strLink *)malloc(sizeof(strLink));
q->data = p1->data;
r->next = q;
r = q;
p1 = p1->next;
}

return tmp;
}

// 串相等
Bool StrEqual(strLink *s,strLink *t)
{
strLink *p1=s->next,*p2=t->next;
if(StrLength(s) != StrLength(t))
return false;

while (p1 != NULL)
{
if(p1->data != p2->data)
return false;

p1=p1->next;
p2=p2->next;
}
return true;
}

leetcode 1300m 转变数组后最接近目标值的数组和

Published on:
Tags: leetcode

题目描述#

给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近  target (最接近表示两者之差的绝对值最小)。

如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。

请注意,答案不一定是 arr 中的数字。

示例1:

1
2
3
输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。

算法#

  1. 排序数组
  2. 遍历数组,找到 $i$ ,使得 $i$ 满足 $sum_{i-1} + arr[i] \times (arrSize - i) > target$ $(sum_i$表示从$0$至$i$的累加和$)$ 。
    找到 $i$ 之后,表示结果 $value < arr[i]$,即从 $i$ 开始以后的元素都使用 $value$ 代替,于是 $value$ 约等于 $(target-sum_{i-1}) \div (arrSize - i)$,说约等于因为这里可能结果不是整数,所以需要根据题意找到最优解(最接近,最小)。
  3. 当 $i==arrSize$ 时,表示数组元素之和小于 $target$ ,返回数组的最大元素即可。

C代码#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <stdlib.h>

void bubble(int* arr, int arrSize);
int findBestValue(int* arr, int arrSize, int target);

int main()
{
int arr[] = {4,9,3};
int n = 3,target = 10;
int value = findBestValue(&arr[0],n,target);
printf("value=%d",value);
return 0;
}
// 排序
void bubble(int* arr, int arrSize)
{
for(int i=0;i<arrSize-1;i++)
{
int flag = 1;
for(int j=arrSize-1;j>i;j--)
{
if(arr[j] < arr[j-1])
{
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
flag = 0;
}
}
if(flag == 1) break;
}
}

int findBestValue(int* arr, int arrSize, int target)
{
bubble(&arr[0],arrSize);
int i,sum=0;

for(i=0;i<arrSize;i++)
{
if(sum + arr[i]*(arrSize-i) > target) break;
sum += arr[i];
}

if(i == arrSize) return arr[arrSize-1];

int value = (target-sum)/(arrSize-i);
double value_1 = (double)(target-sum)/(arrSize-i);
if(value_1 - value > 0.5)
return value+1;
else
return value;
}

时间复杂度#

算法的时间复杂度为排序算法时间复杂度 $O(n^2)$

风雨天堂顶

Published on:
Tags: 爬山 游玩

最近特别想去爬山,正好在朋友圈看到徒步天堂顶的报团活动,于是就拉上朋友一起参加了。

出发前一天,和朋友一起去超市买水果和零食。想到上次因为食物不足导致后期体力不支,就不由的想买多一点。

当天早上7:30出发,整个团有50人。考虑到人数多,体力也各不一样,所以随行有三个领队,按不同的速度行进,尽量的照顾到每个人。
到达良口镇时已经9点多了,大家都跟着领队做一遍热身后就开始上山了。

一开始上山的路还算好走,沿途风景很不错,红花绿叶蜜蜂,小桥流水院子,悠闲宁静。
虽然没有蓝天白云,但是满眼的都是青山绿水,感觉很舒服。

沿途会经过一处漂流点,这才想起来,从化也是有漂流的。
经过漂流点之后就正式上山了。

上山的路就不太好走了。
可能是最近持续下雨的缘故,地面露出了很多石头和树根。
甚至有的是尖锐的石头,有个小伙伴在下坡的时候,不小心就被石头划到了手指,经过简单包扎后,幸好无大碍。最后看他拿出了手套戴上,大家就都明白了——老司机翻车了。
相比于火炉山凤凰山的路,很明显这种的路更加有趣,但是相对的也比较危险。

上山没多久就遇到了急升坡,路本来就难走,加上又陡又长的坡,于是大队人马都堵在了坡上。
因为不好崔前面的人,又没有其他的路,于是大家只能耐心的排着队上坡。
现在想起来,即使当时下着雨,也没有听见有一个人催促快点,这种情况在挤公交挤地铁的时候是绝不可能看到的。可能亲近了大自然,心性也变得没那么燥了。

上了急升坡后,到达一个视野比较开阔的地方。
天气还是多云,虽然看不到远处的风景,但是看到郁郁葱葱的山谷,蒙蒙细雨伴随着阵阵凉风,把疲倦都吹跑了,令人神清气爽。

中午在海拔1000米的附顶用餐,冰镇西瓜,可乐,水果还有各种零食,大家都发挥了分享精神。我们带了足够的水,加上天气没那么热,所以拿出了500ml水给有需要的人。

填饱肚子之余不忘与陌生的同伴闲聊几句,分享沿途的趣事。

用餐后休息一下,就向海拔1200米的顶峰出发了。
可能是一路有细雨的原因,状态越走越好,感觉很快的就到达了顶峰。

到达顶峰自然是各种拍拍拍,大家都排着队和块牌子拍照,哈哈哈……
顶峰的雾很大,能见度很低,能看到的风景很有限,于是玩了一下就去下一站了。

天堂顶峰
天堂顶峰

猴子岭就在距离天堂顶不到100米的地方,到了后同样是拍拍拍,短暂逗留一下就准备下山了。

猴子岭峰
猴子岭峰

下山的途中拍到一处美景,不过很快就消失了,又变成了一大片乌云。

因为云时刻都在飘动,要刚好遇到云都被吹开了,看到了蓝天,看到了太阳光在云上的折射,还是挺难得的。

下山的速度感觉挺快的。
可能注意力都在欣赏美景和寻找美景这件事上,所以反而没有感觉到路有多难走。

下山后需要继续走7公里的机耕路,才能到达影村。于是在山下补充一波食物后,开始往机耕路方向出发。

机耕路跟我想象中的完全不一样,全程都是下坡路,全程都布满了乱石,有些路段还出现了部分塌陷,有够惊险的。

其中几处路口被倒下的树木,竹子给挡住了,必须要钻过去,如果不是只有唯一一条路,差点都以为走到了绝境。

机耕路
机耕路

第一次走机耕路走到失去耐心。期间问了自己好多次,爬山的目的是什么?走天堂顶和走凤凰山有什么区别?走个容易的不香吗。。。

厌走情绪
厌走情绪

下午6点多的时候,终于到达影村。

影村
影村

再走10分钟就到达了驿站,看到先到达的小伙伴已经洗澡好吃饱在玩手机了。

这时候才知道,驿站提供洗浴服务,一次10块钱。早知道就带套干净衣服,带个拖鞋过来了,岂不美哉。

晚上7点多的时候,尾队的小伙伴才陆续到达。

驿站
驿站

回家后发现,这次走天堂顶好像没看到什么美景。

怎么别人看到的是蓝天白云,重峦叠嶂,色彩斑斓,云海林海……,我看到的却是大雾锁山,细雨带风……
我可能去了个“假的天堂顶”。

此时一个“可怕”的想法油然而生,找哪天天气好的,再去一次,真香啊。

也许这就是去天堂顶的原因吧,因为那里有未知的美景在等着我,因为未知,才值得期待。

队列结构——顺序队列、循环队列、链队列

Published on:

队列结构的定义和属性#

定义:队列是一种与线性表相似的线性结构。但仅允许在表的一端进行插入,而在表的另一端进行删除。

队列结构的几个属性:

  • 队尾:插入的一端叫做队尾
  • 队头:删除的一端叫做队头
  • 入队:向队列中插入新节点元素称为入队
  • 出队:从队列中删除节点元素称为出队
  • 队空:当队列中无节点元素时,表示队空
  • 队满:当队列中节点超过允许存储空间时,表示队满(链队无队满的状态)

从队列的基本定义和操作来看,队列是一种具有先进先出特点的数据结构。

队列结构的抽象数据类型#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ADT Queue
{
数据对象:
D = {ai | ai∈ElemType, i=1,2,…,n, n≧0 } //ElemType为类型标识符
数据关系:
R = {<ai, ai+1> | ai, ai+1∈D, i=1,3,…,n-1 }
数据操作:
(1) queSeq * InitQueue(); //初始化队列
(2) void DestroyQueue(queSeq *q); //销毁队列
(3) Bool QueueEmpty(queSeq *q); //判断队列是否为空
(4) int QueueLength(queSeq *q); //返回队列中数据元素个数
(5) void enQueue(queSeq *q, ElemType e); //入队
(6) Bool deQueue(queSeq *q, ElemType *e); //出队
}

可以发现,相比于线性表,队列的抽象运算和栈同样受到限制。

顺序队列#

顺序队列利用数组存放节点元素,同时设置两个变量 frontrear 分别保存队头和队尾的索引。

front 保存队头索引,即是数组索引小的一端; rear 保存队尾索引,即是数组索引大的一端。

定义顺序队列的存储结构:

1
2
3
4
5
6
7
#define MaxSize 50
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int front, rear; // 队头和队尾指针
} queSeq;

初始化队列时,设置变量 front = rear = -1,表示队列为空。

队列执行增删操作时,都需要先把 frontrear 向前移动一位,然后再执行取值或赋值。

由此可知,rear 位置保存队尾元素,front 位置为空,front+1 位置保存队头元素。

rear = MaxSize - 1 时,表示数组已达到存储上限,即是队满了。

front = rear 时,表示队头和队尾保存同一个位置,因为逻辑上 front 位置为空,所以队列为空。

front 位置物理上不为空,因为并没有释放该位置的内存,只是逻辑上认为是空的。

顺序队列代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0
#define MaxSize 50

typedef int Bool;
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int front, rear; // 队头和队尾指针
} queSeq;

// 初始化顺序队列
queSeq * InitQueue();
// 销毁顺序队列
void DestroyQueue(queSeq *q);
// 判断顺序队列是否为空
Bool QueueEmpty(queSeq *q);
// 返回队列中元素个数,也称队列长度
int QueueLength(queSeq *q);
// 进队
Bool enQueue(queSeq *q, ElemType e);
// 出队
Bool deQueue(queSeq *q, ElemType *e);


int main()
{
ElemType e;
queSeq *q;
printf("(1)初始化队列q\n");
q = InitQueue();
printf("(2)依次进队列元素a,b,c\n");
if (enQueue(q, 'a') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'b') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'c') == 0)
printf("队满,不能进队\n");
printf("(3)队列为%s\n", (QueueEmpty(q) == 1? "空" : "非空"));
if (deQueue(q, &e) == 0)
printf("队空,不能出队\n");
else
printf("(4)出队一个元素%c\n", e);
printf("(5)队列q的元素个数:%d\n", QueueLength(q));
printf("(6)依次进队列元素d,e,f\n");
if (enQueue(q, 'd') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'e') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'f') == 0)
printf("队满,不能进队\n");
printf("(7)队列q的元素个数:%d\n", QueueLength(q));
printf("(8)出队列序列:");
while (!QueueEmpty(q))
{
deQueue(q, &e);
printf("%c ", e);
}
printf("\n");
printf("(9)释放队列\n");
DestroyQueue(q);
return 0;
}

// 初始化顺序队列
queSeq * InitQueue()
{
queSeq *tmp = (queSeq *)malloc(sizeof(queSeq));
tmp->front = tmp->rear = -1;
return tmp;
}

// 销毁顺序队列
void DestroyQueue(queSeq *q)
{
free(q);
}

//是否队空
Bool QueueEmpty(queSeq *q)
{
if(q->front == q->rear)
return true;
else
return false;
}

//队长
int QueueLength(queSeq *q)
{
return q->rear - q->front;
}

//入队
Bool enQueue(queSeq *q, ElemType e)
{
//是否队满
if(q->rear == MaxSize - 1)
return false;

q->rear++;
q->data[q->rear] = e;
return true;
}

//出队
Bool deQueue(queSeq *q, ElemType *e)
{
//是否队空
if(QueueEmpty(q))
return false;

q->front++;
*e = q->data[q->front];
return true;
}

循环队列#

顺序队列使用数组存储元素节点,随着队列增删元素,frontrear 的值会递增。

rear = frontrear = MaxSize-1 同时出现时,队列会出现队满的假象。

循环队列充分地使用数组的存储空间,把数组的两端连接起来(相当于把队头和队尾连接起来),形成一个环形的顺序表。从逻辑结构来看这是一个环形队列或循环队列,但是从数据存储的物理结构来看还是和原来的数组方式是一样的。

初始化队列时,设置变量 front = rear = 0,表示队列为空。

因为 front 位置默认为空,,即队列中 front 指向的位置始终为空,所以循环队列的最大存储数量为 MaxSize-1

rear 向前移动一位与 front 相等时表示队满。这也是初始化是 frontrear 不能等于 -1 的原因。

循环队列代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0
#define MaxSize 5

typedef int Bool;
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int front, rear; // 队头和队尾指针
} queCyc;

// 初始化循环队列
queCyc * InitQueue();
// 销毁循环队列
void DestroyQueue(queCyc *q);
// 判断循环队列是否为空
Bool QueueEmpty(queCyc *q);
// 返回队列中元素个数,也称队列长度
int QueueLength(queCyc *q);
// 进队
Bool enQueue(queCyc *q, ElemType e);
// 出队
Bool deQueue(queCyc *q, ElemType *e);


int main()
{
ElemType e;
queCyc *q;
printf("(1)初始化队列q\n");
q = InitQueue();
printf("(2)依次进队列元素a,b,c\n");
if (enQueue(q, 'a') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'b') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'c') == 0)
printf("队满,不能进队\n");
printf("(3)队列为%s\n", (QueueEmpty(q) == 1? "空" : "非空"));
if (deQueue(q, &e) == 0)
printf("队空,不能出队\n");
else
printf("(4)出队一个元素%c\n", e);
printf("(5)队列q的元素个数:%d\n", QueueLength(q));
printf("(6)依次进队列元素d,e,f\n");
if (enQueue(q, 'd') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'e') == 0)
printf("队满,不能进队\n");
if (enQueue(q, 'f') == 0)
printf("队满,不能进队\n");
printf("(7)队列q的元素个数:%d\n", QueueLength(q));
printf("(8)出队列序列:");
while (!QueueEmpty(q))
{
deQueue(q, &e);
printf("%c ", e);
}
printf("\n");
printf("(9)释放队列\n");
DestroyQueue(q);
return 0;
}

// 初始化循环队列
queCyc * InitQueue()
{
queCyc *tmp = (queCyc *)malloc(sizeof(queCyc));
tmp->front = tmp->rear = 0;
return tmp;
}

// 销毁循环队列
void DestroyQueue(queCyc *q)
{
free(q);
}

//是否队空
Bool QueueEmpty(queCyc *q)
{
if(q->front == q->rear)
return true;
else
return false;
}

//队长
int QueueLength(queCyc *q)
{
if(q->rear >= q->front)
return q->rear - q->front;
else
return q->rear + MaxSize - q->front;
}

//入队
Bool enQueue(queCyc *q, ElemType e)
{
//是否队满
if((q->rear+1)%MaxSize == q->front)
return false;

q->rear++;
q->data[q->rear] = e;
return true;
}

//出队
Bool deQueue(queCyc *q, ElemType *e)
{
//是否队空
if(QueueEmpty(q))
return false;

q->front++;
*e = q->data[q->front];
return true;
}

链队列#

链队的存储结构定义为:

1
2
3
4
5
6
7
8
9
10
11
12
typedef char ElemType;
typedef struct node
{
ElemType data;
struct node *next;
} queNode; //链队数据结点类型定义

typedef struct
{
queNode *front;
queNode *rear;
} queLink;

链队由两部分组成,分别是:数据节点组成的数据链队两个指向数据节点的指针
数据节点保存数据和后继节点的地址,两个指针分别指向队头和队尾的数据节点。

初始化队列时,设置指针变量 front = rear = NULL,表示队列为空。

frontrear 不为空时, front 指向队头节点, rear 指向队尾节点。

rear 为空时,表示队列为空。

不能使用 front = rear 作为 队列为空 的判断依据,因为当 front = rear 时,有可能队列里还有一个元素。

链队代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0

typedef int Bool;
typedef char ElemType;
typedef struct node
{
ElemType data;
struct node *next;
} queNode; //链队数据结点类型定义

typedef struct
{
queNode *front;
queNode *rear;
} queLink; //链队类型定义


queLink * InitQueue(); //初始化链队
void DestroyQueue(queLink *q); //销毁链队
Bool QueueEmpty(queLink *q); //判断链队是否为空
int QueueLength(queLink *q); //返回队列中数据元素个数
void enQueue(queLink *q, ElemType e); //入队
Bool deQueue(queLink *q, ElemType *e); //出队

int main()
{
ElemType e;
queLink *q;
printf("(1)初始化链队q\n");
q = InitQueue();
printf("(2)依次进链队元素a,b,c\n");
enQueue(q, 'a');
enQueue(q, 'b');
enQueue(q, 'c');
printf("(3)链队为%s\n", (QueueEmpty(q) == 1 ? "空" : "非空"));
if (deQueue(q, &e) == 0)
printf("队空,不能出队\n");
else
printf("(4)出队一个元素%c\n", e);
printf("(5)链队q的元素个数:%d\n", QueueLength(q));
printf("(6)依次进链队元素d,e,f\n");
enQueue(q, 'd');
enQueue(q, 'e');
enQueue(q, 'f');
printf("(7)链队q的元素个数:%d\n", QueueLength(q));
printf("(8)出链队序列:");
while (!QueueEmpty(q))
{
deQueue(q, &e);
printf("%c ",e);
}
printf("\n");
printf("(9)释放链队\n");
DestroyQueue(q);
return 0;
}

//初始化
queLink * InitQueue()
{
queLink *tmp = (queLink *)malloc(sizeof(queLink));
//默认头尾指针同时为NULL
tmp->front = tmp->rear = NULL;
return tmp;
}

//销毁
void DestroyQueue(queLink *q)
{
queNode *node = q->front,*tmp;
while(node != NULL)
{
tmp = node;
//node节点继续向后移动
node = node->next;
free(tmp);
}
free(node);
free(q);
}

//队列是否为空
Bool QueueEmpty(queLink *q)
{
if(q->rear == NULL)
return true;
else
return false;
}

//队列长度
int QueueLength(queLink *q)
{
queNode *node = q->front;
int n = 0;
while(node != NULL)
{
n++;
node = node->next;
}
return n;
}

// 入队
void enQueue(queLink *q, ElemType e)
{
queNode *node = (queNode *)malloc(sizeof(queNode));
node->data = e;
node->next = NULL;
//空队列时,把新节点赋值给头尾指针
//当不为空队列时,把新节点赋值给尾指针的next指针,并把尾指针向后移动
if(QueueEmpty(q))
q->front = q->rear = node;
else
{
q->rear->next = node;
q->rear = node;
}
}

//出队
Bool deQueue(queLink *q, ElemType *e)
{
queNode *node;
//空队列时,直接return
if(QueueEmpty(q))
return false;

node = q->front;
//只剩下一个节点时,把头尾指针重新赋值为NULL
//剩余多个节点时,把头节点响后移动
if (node->next == NULL)
q->front = q->rear = NULL;
else
q->front = q->front->next;

//节点取值,通过指针传值
*e = node->data;
free(node);
return true;
}