第一类:链表的考察(链表的插入,删除,排序和逆转等)
2012-06-12 00:38
435 查看
数据的存储结构在计算机中主要有四种基本的存储方式:顺序存储、链接存储、索引存储、散列存储方式。我们知道,顺序存储的优点是支持随机访问结构中的数据,但是在插入和删除方面运算时会造成大量数据的移动,效率较低;而链接存储却有效的补充了顺序存储的不足,当需要频繁的插入和删除时,效率得到提高。而索引存储附件了索引表,在访问时通过索引项(由关键字和地址组成值对)去访问数据的存储地址,而散列存储的就是hash的存储方式,即根据关键字通过hash转换求的数据的存储地址。在面试的过程中,链表是其中重要考察内容,下面让我们来一起学习和交流常见的链表操作。
首先,让我们重温一下单链表的数据结构:
一、在单链表的实现的基本运算操作
这些基本操作包括:1.查找单链表中的第i个结点 2.返回指定元素的索引号 3.单链表的插入和删除,这些都是最基本的操作,大家一定要熟练,直接上码图!
二、用链表实现对栈和队列
这是个基础也是非常重要的算法实现问题,既能考察对栈和队列的理解又考察了对链表的操作,因此,也是面试中应该着重学习的地方。
2.1 链栈的数据操作
2.2链队列的数据操作
三、链表的综合操作
3.1.逆向链表的实现 ReverseList(ListNode &head)
思路:通过改变指针指向实现,代码如下:
3.2尽量用最小的时间消耗完成对中间结点的查询
思路:用快慢指针实现,快指针步长为2,慢指针步长为1,同时遍历,当快指针遍历完成时,慢指针指向就是中间结点,复杂度为O(n),代码如下
3.3用最小空间和时间找出该链表的倒数第m个元素
思路:同样可以采用双指针的,一个指针先遍历m个元素,然后第二个指针与第一个指针同时遍历链表,直到第一个指针遍历结束,则第二个指针遍历到的位置即为倒数第m个元素。代码如下
3.4如何判断一个单向链表是否存在循环
思路:一、使用步长法判断,用不同步长遍历两次,看是否遇见。遇见则证明有环;二、如果链表中当前的node的下一个node不是前面的任何一个访问后的node,那么则没有环,否则有环。下面是思路一的代码
结束!小弟也在学习中,希望通过自己学到的和大家进行交流,如有不准确和正确的地方,请各位指正,小弟不甚感激!!^_^
首先,让我们重温一下单链表的数据结构:
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; }
结束!小弟也在学习中,希望通过自己学到的和大家进行交流,如有不准确和正确的地方,请各位指正,小弟不甚感激!!^_^
相关文章推荐
- 单链表的建立、测长、打印、删除节点、插入节点、排序、逆转
- 单链表的建立,测长,打印,删除,插入,排序,逆置
- 链表的创建 查找 排序 插入 删除 逆序 长度 显示
- 单链表的基本操作:建立,求长度,输出,排序,插入,删除,逆置
- 链表,创建,删除,插入,反转,排序
- C++链表的创建、插入、删除、查找、合并、排序、修改等操作的实现
- 链表的基本操作(插入,删除,排序、逆置等)
- 数据结构链表的操作集合(建立,遍历,插入,删除,排序,长度,空判断等)
- 创建链表,遍历,求长度,插入,删除,排序,是否为空
- 【回忆c语言】从指针的定义初始化到链表的插入删除排序实现code
- 单链表的建立,测长,打印,删除,插入,排序,逆置
- C语言单向动态链表程序,实现链表的建立,合并,重新排序,链表元素的插入与删除,以及根据元素成员的值进行元素删除。
- 单链表的建立、测长、删除、插入、排序、逆置及打印(数据结构)
- 数据结构----单向链表之 新建-插入-删除-排序(选择法)-合并-删除-销毁
- 单链表插入删除排序
- 数据结构之单向链表操作1-(插入,删除,交换,反转,排序等操作)
- 链表的插入、删除、排序、追加等源码
- 数据结构:单链表(二)之链表节点排序,升序插入数据,删除指定的所有节点,翻转链表操作
- 链表的逆置、合并、排序以及插入删除
- 链表:插入,删除,显示,排序