小甲鱼数据结构——第三天

视频17-22——线性表

循环链表

单链表,由于每个结点只存储了向后的指针,到了尾部标识就停止了向后链的操作。也就是说,按照这样的方式,只能索引后继结点不能索引前驱结点。所以如果不从头结点出发,就无法访问到全部结点

循环链表:将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。

ps:(1)循环链表不一定要有头结点。(2)循环链表和单链表的主要差异就在于循环的判断空链表的条件上,原来判断head->next是否为null,现在则是head->next是否等于head。(3)如果终端结点用尾指针rear指示,则查找终端结点是O(1),而开始结点是rear->next->next,当然也是O(1)。循环链表中通常以尾指针rear开始。

例子:采用C语言实现循环链表的初始化,插入,删除,遍历读取

#include <stdio.h>
#include <stdlib.h>

/*链表存储结构的定义*/
typedef struct CLinkList
{
    int data;
    struct CLinkList *next;
}node;

/************************************************************************/
/* 操作                                                                  */
/************************************************************************/

/*初始化循环链表*/
void ds_init(node **pNode)
{
    int item;
    node *temp;
    node *target;

    printf("输入结点的值,输入0完成初始化\n");

	while(1)
	{
        scanf("%d", &item);
        fflush(stdin);//存在C语言中,作用是清空标准输入缓冲区里多余的数据。

		if(item == 0)//程序输入结束
            return;
        这里假设pNode为头结点来编程
        if((*pNode) == NULL)/*循环链表中只有一个结点*/
		{
			*pNode = (node*)malloc(sizeof(struct CLinkList));//申请新的结点
			if(!(*pNode))//申请新的结点错误
				exit(0);
            //向新的结点写入数据元素及next指向的地址
			(*pNode)->data = item;//数据域
			(*pNode)->next = *pNode;//指针域
		}
        else
		{
            /*找到next指向第一个结点的结点,找到的这个结点就是尾部结点*/
            for(target = (*pNode); target->next != (*pNode); target = target->next)
				;

            /*生成一个新的结点*/
            temp = (node *)malloc(sizeof(struct CLinkList));
			if(!temp)//申请新的结点错误
				exit(0);

			temp->data = item;
            temp->next = *pNode;//将建立的新的临时结点指向头结点,即将该结点插入在尾部

            target->next = temp;//原来链表的尾部结点指向这个新插入的结点
        }
    }
}

/*插入结点*/
/*参数:链表的第一个结点,插入的位置*/
void ds_insert(node **pNode , int i)
{
    node *temp;
    node *target;
    node *p;
    int item;
    int j = 1;

    printf("输入要插入结点的值:");
    scanf("%d", &item);

    if(i == 1)
	{ //新插入的结点作为第一个结点
        temp = (node *)malloc(sizeof(struct CLinkList));

		if(!temp)
            exit(0);

		temp ->data = item;

        /*寻找到最后一个结点*/
        for(target = (*pNode); target->next != (*pNode); target = target->next)
			;

		temp->next = (*pNode);//将这个新建立的结点指向头结点,作为新的头结点
        target->next = temp;//原来链表的尾部结点指向这个新插入的结点
        *pNode = temp;//因为此时temp为新的第一个结点,所以重新命名第一个新节点的名称
    }
    else
	{
        target = *pNode;
        //for循环执行结束后,target指向第i个元素
        for( ; j < (i-1); ++j )
		{
			target=target->next;
		}


		temp = (node *)malloc(sizeof(struct CLinkList));
		if(!temp)
            exit(0);

		temp ->data = item;
        p = target->next;//p为临时结点,存放原来第i个结点指向的地址[即第i+1个结点]
        target->next = temp;//原来指向第i个结点变为指向新插入的结点
        temp->next = p;//原来第i个结点指向变为原来第i+1个结点地址
    }
}

/*删除结点*/
void ds_delete(node **pNode, int i)
{
    node *target;
    node *temp;
    int j = 1;

    if(i == 1)//删除的是第一个结点
	{
        /*找到最后一个结点*/
        for(target = *pNode; target->next != *pNode;target = target->next)
			;

		temp = *pNode;
        *pNode = (*pNode)->next;//将第二个结点重定义为头结点
        target->next = *pNode;//将尾结点指向新定义的头结点[即原来的第二个结点]
        free(temp);//释放原来的头结点
    }
    else
	{
        target = *pNode;
        //for循环执行结束后,target指向第i个元素
		for( ; j < i-1; ++j )
		{
			target = target->next;
		}

		temp = target->next;//将第i个结点存放在temp中
        target->next = temp->next;
        free(temp);
    }
}

/*返回结点元素所在位置*/
int ds_search(node *pNode, int elem)
{
    node *target;
    int i = 1;

    for(target = pNode; target->data != elem && target->next != pNode; ++i)
	{
		target = target->next;
	}

	if(target->next == pNode) /*表中不存在该元素*/
        return 0;
    else
        return i;
}

/*遍历*/
void ds_traverse(node *pNode)
{
    node *temp;
    temp = pNode;
    printf("***********链表中的元素******************\n");

	do
	{
        printf("%4d ", temp->data);
    }while((temp = temp->next) != pNode);

	printf("\n");
}

int main()
{
    node *pHead = NULL;//初始化头结点为空
    char opp;//存储操作代号
    int find;//查找的位置

    printf("1.初始化链表 \n\n2.插入结点 \n\n3.删除结点 \n\n4.返回结点位置 \n\n5.遍历链表  \n\n0.退出 \n\n请选择你的操作:");
    while(opp != '0')
	{
        scanf("%c", &opp);
        switch(opp)
		{
            case '1':
                ds_init(&pHead);
                printf("\n");
                ds_traverse(pHead);
                break;

            case '2':
                printf("输入需要插入结点的位置?");
                scanf("%d",  &find);
                ds_insert(&pHead, find);
                printf("在位置%d插入值后:\n",  find);
                ds_traverse(pHead);
                printf("\n");
                break;

            case '3':
                printf("输入需要删除的结点位置?");
                scanf("%d",  &find);
                ds_delete(&pHead, find);
                printf("删除第%d个结点后:\n",  find);
                ds_traverse(pHead);
                printf("\n");
                break;

            case '4':
                printf("你要查找倒数第几个结点元素的值?");
                scanf("%d",  &find);
                printf("元素%d所在位置:%d\n",  find,  ds_search(pHead, find));
                //ListTraverse(L);
                printf("\n");
                break;

            case '5':
                ds_traverse(pHead);
                printf("\n");
                break;

            case '0':
                exit(0);
        }
    }

    return 0;
}

约瑟夫问题

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

问题:采用循环链表的方式依次输出死亡顺序

#include <stdio.h>
#include <stdlib.h>

/**<创建链表结构存储  */
typedef struct node
{
    int data;
    struct node *next;
}node;

/** \brief 创建一个循环链表
 *
 * \param n:循环链表的长度
 * \return 第一个结点的地址
 *
 */
node *create(int n)
{
	node *p = NULL, *head;
	head = (node*)malloc(sizeof (node ));//head头结点
	p = head;
	node *s;
	int i = 1;

	if( 0 != n )
	{
		while( i <= n )
		{
			s = (node *)malloc(sizeof (node));
			s->data = i++;    // 为循环链表初始化,第一个结点为1,第二个结点为2。
			p->next = s;
			p = s;
		}
		s->next = head->next;//将头结点的指向[即第一个结点]赋值给最后一个结点的指向
	}

	free(head);//释放头结点。此后循环链表中不存在头结点

	return s->next ;
}

int main()
{
	int n = 41;
	int m = 3;
	int i;
	node *p = create(n);
	node *temp;

	m %= n;   // m在这里是等于2

	while (p != p->next )//循环结束条件为自己指向自己,即此时仅存在一个结点
	{
		for (i = 1; i < m-1; i++)//此时循环仅执行一次
		{
			p = p->next ;
		}

		printf("%d->", p->next->data );
        //删除第m个节点:将第m-1个结点的指向直接指向第m+1结点的位置
		temp = p->next ;//第m-1结点的指向,即第m结点
		p->next = temp->next ;//原来第m结点指向赋值给第m-1结点的指向
		free(temp);//释放原来第m结点

		p = p->next ;//因为循环需要,此处需要特别注意!!!!
	}

	printf("%d\n", p->data );

	return 0;
}

变式:编号为1~N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数,可以自由输入),开始人选一个正整数作为报数上限值M,从第一个人按顺时针方向自1开始顺序报数,报道M时停止报数。报M的人出列,将他的密码作为新的M值,从他顺时针方向上的下一个人开始从1报数,如此下去,直至所有人全部出列为止

 

 

 

例题:实现将两个线性表(a1,a2,…,an)和(b1,b2,…,bm)连接成一个线性表(a1,…,an,b1,…bm)的运算。

分析:(1)若在单链表或头指针表示的单循环表上做这种链接操作,都需要遍历第一个链表,找到结点an,然后将结点b1链到an的后面,其执行时间是O(n)。(2)若在尾指针表示的单循环链表上实现,则只需修改指针,无须遍历,其执行时间是O(1)

//假设A,B为非空循环链表的尾指针
LinkList Connect(LinkList A,LinkList B)
{	
	LinkList p = A->next;		//保存A表的头结点位置
	
	A->next = B->next->next;	//B表的开始结点链接到A表尾
	
	free(B->next);	//释放B表的头结点,初学者容易忘记
	
	B->next = p;		
	
	return B;		//返回新循环链表的尾指针
}

判断单链表中是否有环?

有环的定义:链表的尾节点指向了链表中的某个节点

判断单链表中是否有环?

法一:使用p、q两个指针,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样。如图,当p从6走到3时,用了6步,此时若q从head出发,则只需两步就到3,因而步数不等,出现矛盾,存在环。【判断条件:两种走法某个结点的地址以及其存储的数据元素是否相等】

法二:使用p、q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环。

/**< 自定义随机产生单链表
    头插法实现无环的单链表
    尾插法实现在第二个结点处有环的单链表
    采用两种方法判断单链表是否存在环

    !!调试过程不允许存在中文路径!!*/
#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
//链表存储结构定义
typedef struct Node
{
    ElemType data;
    struct Node *next;
}Node, *LinkList;

/* 初始化带头结点的空链表 */
Status InitList(LinkList *L)
{
    *L = (LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */

    if(!(*L)) /* 存储分配失败 */
       return ERROR;

    (*L)->next=NULL; /* 指针域为空 */

    return OK;
}

/* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{
    int i=0;
    LinkList p=L->next; /* p指向第一个结点 */
    while(p)
    {
        i++;
        p=p->next;
    }
    return i;
}

/*  随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
void CreateListHead(LinkList *L, int n)
{
	LinkList p;
	int i;

	srand(time(0));                         /*  初始化随机数种子 */

	*L = (LinkList)malloc(sizeof(Node));
	(*L)->next = NULL;                      /*  建立一个带头结点的单链表 */

	for (i=0; i < n; i++)
	{
		p = (LinkList)malloc(sizeof(Node)); /*  生成新结点 */
		p->data = rand()%100+1;             /*  随机生成100以内的数字 */
		p->next = (*L)->next;
		(*L)->next = p;						/*  插入到表头 */
	}
}

/*  随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
	LinkList p,r;
	int i;

	srand(time(0));                      /* 初始化随机数种子 */
	*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
	r = *L;                              /* r为指向尾部的结点 */

	for (i=0; i < n; i++)
	{
		p = (Node *)malloc(sizeof(Node)); /*  生成新结点 */
		p->data = rand()%100+1;           /*  随机生成100以内的数字 */
		r->next=p;                        /* 将表尾终端结点的指针指向新结点 */
		r = p;                            /* 将当前的新结点定义为表尾终端结点 */
	}

    r->next = (*L)->next->next;//为了给链表添加环[在第二个结点处添加环]
}

// 比较步数的方法
int HasLoop1(LinkList L)
{
    LinkList cur1 = L;  // 定义结点 cur1
    int pos1 = 0;       // cur1 的步数

    while(cur1)
    {                       // cur1 结点存在
        LinkList cur2 = L;  // 定义结点 cur2
        int pos2 = 0;       // cur2 的步数
        while(cur2)
        {                           // cur2 结点不为空
            if(cur2 == cur1)
            {                       // 当cur1与cur2到达相同结点时
                if(pos1 == pos2)    // 走过的步数一样
                    break;          // 说明没有环
                else                // 否则
                {
                    printf("环的位置在第%d个结点处。\n\n", pos2);
                    return 1;       // 有环并返回1
                }
            }
            cur2 = cur2->next;      // 如果没发现环,继续下一个结点
            pos2++;                 // cur2 步数自增
        }
        cur1 = cur1->next;  // cur1继续向后一个结点
        pos1++;             // cur1 步数自增
    }
    return 0;
}

// 利用快慢指针的方法【该方法比较好一点】
int HasLoop2(LinkList L)
{
    LinkList p = L;
    LinkList q = L;

    while (p != NULL && q != NULL && q->next != NULL)
    {
        p = p->next;//p走一步
        if (q->next != NULL)
            q = q->next->next;//q走两步

        printf("p:%d, q:%d \n", p->data, q->data);

        if (p == q)
            return 1;
    }
    return 0;
}

int main()
{
    LinkList L;
    Status i;
    char opp;

    i = InitList(&L);
    printf("初始化L后:ListLength(L)=%d\n",ListLength(L));

    printf("\n1.创建有环链表(尾插法) \n2.创建无环链表(头插法) \n3.判断链表是否有环 \n0.退出 \n\n请选择你的操作:\n");
    while(opp != '0')
    {
        scanf("%c",&opp);
        switch(opp)
        {
            case '1':
                CreateListTail(&L, 10);
                printf("成功创建有环L(尾插法)\n");
                printf("\n");
                break;

            case '2':
                CreateListHead(&L, 10);
                printf("成功创建无环L(头插法)\n");
                printf("\n");
                break;

            case '3':
                printf("方法一: \n\n");
                if( HasLoop1(L) )
                {
                    printf("结论:链表有环\n\n\n");
                }
                else
                {
                    printf("结论:链表无环\n\n\n");
                }

                printf("方法二:\n\n");
                if( HasLoop2(L) )
                {
                    printf("结论:链表有环\n\n\n");
                }
                else
                {
                    printf("结论:链表无环\n\n\n");
                }
                printf("\n");
                break;

            case '0':
                exit(0);
        }
    }

}

调试程序【使用CodeBlock软件】

(1)CodeBlocks的debug只有在项目里才能用,所以说我们要新建一个项目,CodeBlocks左上角File->new->Project->Console application,然后创建一个project。

(2)而且debug功能本身也是要设置的(在不能debug的情况限定, 可以用就不需要设置),设置流程Settings->Debugger...->Default->Executable path->自己CodeBlocks的安装位置->MinGW->bin->gdb32.exe【该设置有可能需要自己设置,否则软件无法调试】

魔术师发牌问题

问题描述:魔术师利用一副牌中的13张黑牌,预先将他们排好后叠放在一起,牌面朝下。对观众说:“我不看牌,只数数就可以猜到每张牌是什么,我大声数数,你们听,不信?现场演示。”魔术师将最上面的那张牌数为1,把他翻过来正好是黑桃A,将黑桃A放在桌子上,第二次数1,2,将第一张牌放在这些牌的下面,将第二张牌翻过来,正好是黑桃2,也将它放在桌子上这样依次进行将13张牌全部翻出,准确无误。
问题:牌的开始顺序是如何安排的?

#include <stdio.h>
#include <stdlib.h>

#define  CardNumber 13

//链表存储结构定义
typedef struct node
{
    int data;
    struct node *next;
}sqlist, *linklist;
/** \brief 创建初始化循环链表,各个数据元素均为0
 *
 * \return 头结点的位置
 *
 */

linklist CreateLinkList()
{
    linklist head = NULL;//头结点
    linklist s, r;
    int i;

    r = head;//尾结点和头结点相同[此时为空表]

    for(i=1; i <= CardNumber; i++)
    {
        s = (linklist)malloc(sizeof(sqlist));
        s->data = 0;

        if(head == NULL)
            head = s;
        else
            r->next = s;

        r = s;
    }

    r->next = head;//最后尾结点指向头结点,构成循环链表

    return head;
}

// 发牌顺序计算
void Magician(linklist head)
{
    linklist p;
    int j;
    int Countnumber = 2;

    p = head;
    p->data = 1;  //第一张牌放1

    while(1)
    {
        for(j=0; j < Countnumber; j++)
        {
            p = p->next;
            if(p->data != 0)  //该位置有牌的话,则下一个位置
            {
                p->next;
                j--;
            }
        }


        if(p->data == 0)
        {
            p->data = Countnumber;
            Countnumber ++;

            if(Countnumber == 14)
                break;
        }
    }
}
//销毁工作[如果程序不长时间运行销毁与否都可以]
void DestoryList(linklist *list)
{
    linklist ptr = *list;
    linklist buffer[CardNumber];
    int i = 0;
    while(i < CardNumber)
    {
        buffer[i++] = ptr;
        ptr = ptr->next;
    }

    for(i = 0; i < CardNumber; ++i)
    {
        free(buffer[i]);
    }

    *list = 0;
    printf("\n链表已经被销毁!!!\n");
}


int main()
{
    linklist p;
    int i;

    p = CreateLinkList();
    Magician(p);

    printf("按如下顺序排列:\n");
    for (i=0; i < CardNumber; i++)
    {
        printf("黑桃%d ", p->data);
        p = p->next;
    }

    DestoryList(&p);
    return 0;
}

拉丁方阵问题

拉丁方阵是一种n×n的方阵,方阵中恰有n种不同的元素,每种元素恰有n个,并且每种元素在一行和一列中恰好出现一次。

1

2

3

2

3

1

3

1

2

分析:观察n×n的方阵可知,其第一行为1,2,3……n;第二行为2,3,4……n,1;第三行为3,4……n,1,2;…………;第n行为n,1,2,3,4…n-1。所以可以采用循环链表思想来解决

#include <stdio.h>
#include <stdlib.h>

//链表存储结构
typedef struct LinkList
{
    int data;
    struct LinkList *next;
}node;
/** \brief 创建初始化循环链表
 *
 * \param 链表L
 * \param 链表的长度n
 * \return 头结点的地址
 *
 */
node* CreateList(node *head, int n)
{
    node *rear;//尾结点
    node *p, *temp;//临时结点
    int i = 1;
    //创建一个结点
    if(head == NULL)
        head = (node*)malloc(sizeof(node));
    if(!head)
        exit(0);

    p = head;
    while(i <= n)
    {
        //创建一个结点
        temp = (node*)malloc(sizeof(node));
        if(!temp)
            exit(0);

        temp->data = i;
        p->next = temp;
        p = temp;
        i++;
    }
    //尾结点指向第一个结点,构成循环链表
    rear = p;
    rear->next = head->next;

    return rear->next;
}
/** \brief 打印输出循环链表
 *
 * \param 头结点的地址
 * \return 无
 *
 */
void print(node *pHead)
{
    node *temp;
    temp = pHead;

    while(1)
    {
        printf("%3d", temp->data);
        temp = temp->next;
        if(temp == pHead)//再次达到第一个结点的位置
            break;
    }
}

int main()
{
    int n;//存放阶数
    node *L = NULL;
    node *pHead;//头结点
    int count = 1;

    printf("请输入拉丁方阵的阶数n = ");
    scanf("%d", &n);

    pHead = CreateList(L, n);
    while(count <= n)
    {
        print(pHead);
        printf("\n");
        pHead = pHead->next;
        count++;
    }

    return 0;
}

双向链表

1.存储结构特点

typedef struct DualNode
{
ElemType data;
struct DualNode *prior;  //前驱结点
struct DualNode *next;   //后继结点
} DualNode, *DuLinkList;

2.双向链表的循环链表

3.插入

//代码实现
s->next = p;	
s->prior = p->prior;	
p->prior->next = s;	
p->prior = s;

4.删除

//代码实现
p->prior->next = p->next;
p->next->prior = p->prior;	
free(p);

PS:(1)双向链表相对于单链表来说,是要更复杂一点,每个结点多了一个prior指针,对于插入和删除操作的顺序大家要格外小心。(2)双向链表可以有效提高算法的时间性能,说白了就是用空间来换取时间

5.双向循环链表实践例子

问题描述:要求实现用户输入一个数使得26个字母的排列发生变化,例如用户输入3,输出结果:DEFGHIJKLMNOPQRSTUVWXYZABC。同时需要支持负数,例如用户输入-3,输出结果:XYZABCDEFGHIJKLMNOPQRSTUVW
Anwser1:单链表的循环链表实现

#include <stdio.h>
#include <stdlib.h>

//链表存储结构
typedef struct LinkList
{
    int data;
    struct LinkList *next;
}node;
/** \brief 创建初始化循环链表
 *
 * \param 链表L
 * \param 链表的长度n
 * \return 头结点的地址
 *
 */
node* CreateList(node *head, int n)
{
    node *rear;//尾结点
    node *p, *temp;//临时结点
    int i = 1;
    char ch = 'A';
    //创建一个结点
    if(head == NULL)
        head = (node*)malloc(sizeof(node));
    if(!head)
        exit(0);

    p = head;
    while(i <= n)
    {
        //创建一个结点
        temp = (node*)malloc(sizeof(node));
        if(!temp)
            exit(0);

        temp->data = ch;
        p->next = temp;
        p = temp;
        i++;
        ch++;
    }
    //尾结点指向第一个结点,构成循环链表
    rear = p;
    rear->next = head->next;

    return rear->next;
}
/** \brief 打印输出循环链表
 *
 * \param 头结点的地址
 * \return 无
 *
 */
void print(node *pHead)
{
    node *temp;
    temp = pHead;

    while(1)
    {
        printf("%c", temp->data);
        temp = temp->next;
        if(temp == pHead)//再次达到第一个结点的位置
            break;
    }
}

int main()
{
    int n;//存放阶数
    int m;
    node *L = NULL;
    node *pHead;//头结点
    int count = 1;

    printf("请输入拉丁方阵的阶数n = ");
    scanf("%d", &n);
    printf("请输入移动的次数m = ");
    scanf("%d", &m);

    pHead = CreateList(L, n);
    printf("\n输出最原始链表数据元素:\n");
    print(pHead);

    printf("\n输出改变后链表数据元素:\n");
    if(m > 0)//左循环移动
    {
        while(count <= m%n)
        {
            pHead = pHead->next;
            count++;
        }
        print(pHead);
    }
    else if(m == 0)
    {
        print(pHead);
    }
    else//右循环移动
    {
        m = abs(m);//取绝对值
        while(count <= (n-m%n))//判断条件举例子分析
        {
            pHead = pHead->next;
            count++;
        }
        print(pHead);
    }

    return 0;
}

Anwser2:双向循环链表实现

#include <stdio.h>
#include <stdlib.h>
/**< Status类型返回的状态值 */
#define OK 1
#define ERROR 0

typedef char ElemType;
typedef int Status;

//双向循环存储结构定义
typedef struct DualNode
{
    ElemType data;//数据域
    struct DualNode *prior;//前驱结点
    struct DualNode *next;//后继结点
}DualNode, *DuLinkList;

Status IninList(DuLinkList *L)
{
    DualNode *p,*q;
    int i;

    //创建头结点
    *L = (DuLinkList)malloc(sizeof(DualNode));
    if(!(*L))
        return ERROR;
    (*L)->next = (*L)->prior = NULL;
    //向双向链表中写入数据
    p = (*L);
    for(i = 0; i < 26; i++)
    {
        q = (DualNode *)malloc(sizeof(DualNode));
        if(!q)
            return ERROR;

        q->data = 'A'+i;
        q->prior = p;
        q->next = p->next;//暂时指向NULL,但是尽量不要直接写q->next = NULL
        p->next = q;

        p = q;//循环需要,重新命名
    }
    //最后一个结点指向第一个结点,构成循环
    p->next = (*L)->next;
    (*L)->next->prior = p;

    return OK;
}

void Caesar(DuLinkList *L, int i)
{
    if(i > 0)
    {
        do
        {
            (*L) = (*L)->next;
        }while(--i);
    }

    if(i < 0)
    {
        //此时(*L)指针时其前驱后继都指向NULL,此时需要注意
        (*L) = (*L)->next;
        do
        {
            (*L) = (*L)->prior;
        }while(i++);
    }

}


int main()
{
   DuLinkList L;
   int i,n;

   IninList(&L);

   printf("请输入一个整数:");
   scanf("%d", &n);
   printf("\n");

   Caesar(&L, n);

   for(i = 0; i < 26; i++)
   {
       L = L->next;
       printf("%c", L->data);
   }

    return 0;
}

课后作业之Vigenere(维吉尼亚)加密

当输入明文,自动生成随机密匙匹配明文中每个字母并移位加密。

明文

I

L

O

V

E

F

I

S

H

C

随机密匙

3

15

23

2

52

1

33

49

13

19

密文

L

A

L

X

E

G

P

P

U

V

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值