您的位置:首页 > 其它

第一类:链表的考察(链表的插入,删除,排序和逆转等)

2012-06-12 00:38 435 查看
数据的存储结构在计算机中主要有四种基本的存储方式:顺序存储、链接存储、索引存储、散列存储方式。我们知道,顺序存储的优点是支持随机访问结构中的数据,但是在插入和删除方面运算时会造成大量数据的移动,效率较低;而链接存储却有效的补充了顺序存储的不足,当需要频繁的插入和删除时,效率得到提高。而索引存储附件了索引表,在访问时通过索引项(由关键字和地址组成值对)去访问数据的存储地址,而散列存储的就是hash的存储方式,即根据关键字通过hash转换求的数据的存储地址。在面试的过程中,链表是其中重要考察内容,下面让我们来一起学习和交流常见的链表操作。

首先,让我们重温一下单链表的数据结构:

typedef int datatype;

typedef struct ListNode{

datatype data;//结点数据域

ListNode *next;//结点指针域

}ListNode,*Linklist;


一、在单链表的实现的基本运算操作

这些基本操作包括:1.查找单链表中的第i个结点 2.返回指定元素的索引号 3.单链表的插入和删除,这些都是最基本的操作,大家一定要熟练,直接上码图!



*
*author:xuzhaohu
functon:单链表的数据结构,查找、插入和删除
*/
#include <iostream>
using namespace std;

typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}ListNode,*LinkList;

//查找单链表中的第i个节点的地址
ListNode * Locate(LinkList head,int i)
{
ListNode *p;
int index = 0;
if(head==NULL)
return NULL;
if(i<0)
return NULL;
p = head;
if(p!=NULL && index<i)
{
p=p->next;
index++;
}
return p;
}
//返回链表中第一个节点元素为 x 的值的索引号
int findValue(LinkList head,datatype x)
{
ListNode *p;
int index = 1;
if(head==NULL)
return 0;
p=head;
if(p!=NULL)
{
if(p->data == x)
return index;
else
{
p=p->next;
index++;
}
}
else
{
cout<<"单链表中没有这个值的元素"<<endl;
return 0;
}
}
//单链表的插入 分表首插入和表中插入
void InsertNode(LinkList &head,int i,datatype x)
{
ListNode *p,*q;
p = Locate(head,i-1);//注意是掺入前面的一个元素
q = new ListNode;
q->data = x;
if(i == 0)
{
q->next = head;
head = q;
}
else
{
q = p->next;
p->next = q;
}
}
//单链表的删除 ,也分表头删除和表中删除
void DeleteNode(LinkList &head,int i)
{
ListNode *p,*q;//q是删除节点p是其前一个节点
p = Locate(head ,i-1);//注意是找到前面一个元素
if(i==0)
{
q = head;
head=q->next;
delete q;
}
else
{	if(p==NULL)//i值给的有问题,索引值溢出后者为负值
cout<<"error"<<endl;
else if(p->next == NULL)//p恰好是最后一个元素
cout<<"no ith node"<<endl;
else
{
q = p->next;
p->next = q->next;
delete q;
}
}
}

int main()
{
return 0;
}


二、用链表实现对栈和队列

这是个基础也是非常重要的算法实现问题,既能考察对栈和队列的理解又考察了对链表的操作,因此,也是面试中应该着重学习的地方。

2.1 链栈的数据操作

/**
* author:xuzhaohu
* function:stackNode 先进后出
* date:
*/
#include<iostream>
using namespace std;
//与单链表相似,不同之处在与插入和删除只限定在表首进行,表头指针就是栈顶元素
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}StackNode,*LinkStack;
// 链栈的插入
void push(LinkStack &top,datatype x)
{
StackNode *p;
p = new StackNode;
//注意:链栈的插入只考虑单链表的表首插入
p->data = x;
p->next = top;
top = p;
}
//链栈的弹出
void pop(LinkStack &top,datatype &x)
{
StackNode *p;
if(top == NULL)
cout<<"underflow"<<endl;
else
{
//链栈的删除只考虑表首的删除
p = top;
x = top->data;
top = top->next;
delete p;
}
}
//读取链栈的栈顶元素
void GetTop(LinkStack top, datatype &x)
{
if(top == NULL)
cout<<"error";
else
x = top->data;
}
//置空链栈
void ClearStack(LinkStack &top)
{
top = NULL;
}
//判断链栈是否为空
int StackEmpty(LinkStack &top)
{
if(top == NULL)
return 1;
else
return 0;
}

int main()
{
return 0;
}


2.2链队列的数据操作

/**
*author:xuzh
*function:链队列的数据结构,先进先出
*/
#include<iostream>
using namespace std;

typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}QueueNode;
typedef struct
{
QueueNode *front;//队头指针
QueueNode *rear;//队尾指针
}LinkQueue;
QueueNode *p,*q;
LinkQueue QU;
datatype x;

//链队列的插入,只能在队尾插入
void EnQueue(LinkQueue &QU,datatype x)
{
QueueNode *p = new QueueNode;
p->data = x;
p->next = NULL;//p为最后一个元素
if(QU.front == NULL)
QU.front = QU.rear = p;
else
{
QU.rear->next = p;//队尾元素变为新的元素p,p为旧的队尾元素的下一个元素
QU.rear = p;
}
}
//链队列的删除,只能在队首操作
void DeQueue(LinkQueue &QU,datatype &x)
{
QueueNode *q;
if(QU.front == NULL)
cout<<"underflow"<<endl;
else
{
q = QU.front;
x = q->data;
QU.front = q->next;
delete q;
}
}
//读栈头元素
void GetFront(LinkQueue &QU,datatype &x)
{
if(QU.front == NULL)
cout<<"error"<<endl;
else
x = QU.front->data;
}
//清空队列
void ClearQueue(LinkQueue &QU)
{
QU.front = QU.rear = NULL;
}
//判定一个队列是否为空
int QueueEmpty(LinkQueue &QU)
{
if(QU.front == NULL)
return 1;
else
return 0;
}
int main()
{
return 0;
}


三、链表的综合操作

3.1.逆向链表的实现 ReverseList(ListNode &head)

思路:通过改变指针指向实现,代码如下:

//reverse the list
void ReverseList(LinkList &head)
{
ListNode *p,*q,*r;//相邻的两个指针
if(head == NULL)
cout<<"error"<<endl;
p = head;
q = p->next;
p->next = NULL;//首节点对应着 逆转后的 最后一个节点
while(q!=NULL)
{
if(q->next==NULL)
head = q;//表首指针指向最后一个元素
r = q->next;
q->next = p;
p = q;
q = r;
}
}


3.2尽量用最小的时间消耗完成对中间结点的查询

思路:用快慢指针实现,快指针步长为2,慢指针步长为1,同时遍历,当快指针遍历完成时,慢指针指向就是中间结点,复杂度为O(n),代码如下

//尽量用最小的时间消耗完成对中间结点的查询
void FindMindleNode(LinkList head)
{
ListNode *fast,*slow;//定义一个快慢指针,一个步长为2,一个步长为1
int index = 1;//中间节点的索引值
if(head == NULL)
{
cout<<"error"<<endl;
return ;
}
slow = fast = head;
if(head->next == NULL)
{
cout<<"the mid node is "<<index<<endl;
return ;
}
slow = slow->next;//慢指针走一步
fast = fast->next;//快指针走两步
while(fast!=NULL && slow!=NULL)
{
index++;
slow = slow->next;
if(fast->next == NULL)//如果fast的下一个指针就已经为空了,说明快指针已经遍历完成
fast = fast->next;//这时候让fast指针指向最后一个元素就行
else
fast = fast->next->next;
}
cout<<"the mid node is "<<index<<endl;
}


3.3用最小空间和时间找出该链表的倒数第m个元素

思路:同样可以采用双指针的,一个指针先遍历m个元素,然后第二个指针与第一个指针同时遍历链表,直到第一个指针遍历结束,则第二个指针遍历到的位置即为倒数第m个元素。代码如下

//get the mth element in the list
int GetLastM(LinkList head, int m)
{
ListNode *fast,*slow;
int i = 0;
fast = slow = head;
if(head == NULL)
return -1;
while(fast!=NULL)
{
i++;
fast = fast->next;
if(i>=m)
{
if(slow!=NULL)
slow = slow->next;
}
}
return slow->data;
}


3.4如何判断一个单向链表是否存在循环

思路:一、使用步长法判断,用不同步长遍历两次,看是否遇见。遇见则证明有环;二、如果链表中当前的node的下一个node不是前面的任何一个访问后的node,那么则没有环,否则有环。下面是思路一的代码

// Circle List 's last element->next = head
bool CircleList(LinkList head)
{
//define two different step pointer,if they meet,there is circle, or not
ListNode *fast,*slow;
if(head == NULL)
return false;
slow = head;
fast = head->next;
while(fast!=NULL && fast->next!=NULL && slow!=fast)
{
slow = slow->next;
fast = fast->next->next;
}
if(slow = fast)
return true;
else
return 0;
}


结束!小弟也在学习中,希望通过自己学到的和大家进行交流,如有不准确和正确的地方,请各位指正,小弟不甚感激!!^_^
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐