您的位置:首页 > 其它

链表常见操作

2010-05-12 22:00 405 查看
面试常会考到链表和C库函数的操作,特地整理下了链表的常用函数,以供参考:
一.链表
链表定义如下:
为了程序的可读性,LinkList表示链表头节点指针,用于表示一个链表,pNode表示节点指针
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>

typedef struct Node
{
int data;
struct Node *next;
}Node,*LinkList,*pNode;
//为了简单起见,我们讲链表值保存到一个数组中,这样链表操作后可以显而易见的看到运行结果是否正确.
int LinkListData[10] = {0,1,2,3,4,5,6,7,8,9};

另外我们创建的链表是带头结点的链表,即单链表的首元结点(存放第一个数据元素的结点)之前附设一个头结点(数据域什么都不放),称之为带头结点的单链表,反之就是不带头结点的单链表。

1.创建链表
(1)顺序创建链表
p是临时节点指针,指向最后一个节点.增加一个节点时实际上就是在链表尾部插入一个新节点.
LinkList CreateListSequence(int num)
{
pNode p,pHead,pEnd;
int i;
if(!(p = pHead = (LinkList)malloc(sizeof(Node))))
{
return NULL;
}
p->next = pHead->next = NULL;
for(i=0;i<num;i++)
{
if(pEnd = ((LinkList)malloc(sizeof(Node))))
{
pEnd->data = LinkListData[i];
pEnd->next = p->next;
p->next = pEnd;
p = pEnd;
}
}
return pHead;
}

(2)逆序创建链表
逆序创建链表时就是将新的节点(pNew)插入到Head节点之后.
LinkList CreateListInverse(int num)
{
LinkList pHead,pNew;
int i;

if(!(pHead=(LinkList)malloc(sizeof(Node))))
{
return NULL;
}
pHead->next = NULL;
for(i=0;i<num;i++)
{
if(pNew=(LinkList)malloc(sizeof(Node)))
{
pNew->data = LinkListData[i];
pNew->next = pHead->next;
pHead->next = pNew;
}
}
return pHead;
}

2.获取某个指定结点的值
int GetElement(LinkList list,int location)
{
int i = 0;
pNode point = list->next;
while(point && i<location)
{
point = point->next;
i++;
}
if(!point || i > location)
return 0;
return point->data;
}

3.插入元素
int ListInsert(LinkList list,int location,int arg)
{
pNode point=list->next,node;
int i=1;
while(i<location)
{
point = point->next;
i++;
}
if(!point || (i > location))
{
printf("/nInsert operatoin failed");
return 0;
}
if(!(node =(LinkList)malloc(sizeof(Node))))
return 0;
node->data = arg;
node->next = point->next;
point->next = node;

return 1;
}

4.删除元素
void ListDelete(LinkList list,int location)
{
LinkList point = list->next;
int i = 1;
while(i < location)
{
point = point->next;
list = list->next;
i++;
}
if(!point || (i > location))
{
printf("/nDelete operation failed!/n");
return;
}
list->next = point->next;
free(point);
}

5.反序链表
最简单就是前插法,将头结点摘下来,然后采用前插法依次插入,例如:
初始: head -> 1 -> 2 -> 3
第一步: head
第二步: head -> 1
第三步: head -> 2 -> 1
第四步: head -> 3 -> 2 -> 1
LinkList ReverseLinkList(LinkList pHead)
{
pNode p = pHead-> next;

pHead->next = NULL;

while(p){
pNode q=p;
p = p-> next;
q->next = pHead-> next;
pHead->next = q;
}

return pHead;
}

6.找出链表中间节点
采用两个节点指针,一个一次移动2个节点,一个一次移动一个节点,这样只需偏历一次链表就可以找到中间节点了.
Node* find_midlist(LinkList pHead)
{
pNode p1, p2;
if(pHead == NULL || pHead->next == NULL)
return pHead;
//链表有头结点,如无头结点,则为p1 = p2 = pHead->next
p1 = p2 = pHead->next;
while (1)
{
if (p2->next != NULL && p2->next->next != NULL)
{
p2 = p2->next->next;

p1 = p1->next;
}
else
{
break;
}
}
return p1;
}

7.判断一个单链表是否有环
这道题是《C专家编程》中的题了.这里采用的算法是一个指针一次走一步,另一个一次走两步,如果链表有环,那么这两个指针一定会相遇.
int is_looplist (LinkList pHead)
{
pNode p1, p2;

p1 = p2 = pHead;

if (pHead == NULL || pHead->next == NULL)
return 0;
while (p2->next != NULL && p2->next->next != NULL)
{
p1 = p1->next;
p2 = p2->next->next;

if (p1 == p2)
return 1;
}
return 0;
}

8.快速排序
void BubbleSort(LinkList list)
{
int temp;
pNode p1,p2;
p1 = list->next;
p2 = p1->next;

while(p1->next)
{
if(p1->data > p2->data)
{
temp = p1->data;
p1->data = p2->data;
p2->data = temp;
}
p2 = p2->next;
if(!p2)
{
p1 = p1->next;
p2 = p1->next;
}
}
}


二.C库函数
1. itoa
数字转换为字符串
static void itoa(int n, char s[])
{
int i = 0;
//获得一个数的绝对值
unsigned int val = (unsigned int)((n < 0) ? -n : n);

//从后向前提取数字
do{
s[i++] = (val % 10) + '0';
}while(val/=10);

//如果数字为负,添加-号
if(n < 0) s[i] = '-';
s[i+1] = '/0';

//颠倒字符串
for(n=0;n<i;n++,i--)
{
char swap = s
;
s
= s[i];
s[i] = swap;
}

}

2.atoi
字符串转换成整型数
int atoi(char s[])
{
int sign=1,num=0;
if(*s=='-')
{
sign=-1;
s++;
}
else if(*s=='+')
{
sign=1;
s++;
}

while((*s)!='/0')
{
num=num*10+(*s-'0');
s++;
}

return num*sign;
}

上面的这个比较简单,下面看看微软的和glibc里的实现
微软:
long atol(const char *nptr)
{
int c; /* current char */
long total; /* current total */
int sign; /* if '-', then negative, otherwise positive */

/* skip whitespace */
while ( isspace((int)(unsigned char)*nptr) )
++nptr;

c = (int)(unsigned char)*nptr++;
sign = c; /* save sign indication */
if (c == '-' || c == '+')
c = (int)(unsigned char)*nptr++; /* skip sign */

total = 0;

while (isdigit(c)) {
total = 10 * total + (c - '0'); /* accumulate digit */
c = (int)(unsigned char)*nptr++; /* get next char */
}

if (sign == '-')
return -total;
else
return total; /* return result, negated if necessary */
}

int atoi(const char *nptr)
{
return (int)atol(nptr);
}

glibc:
#define LONG_MAX 2147483647L
#define LONG_MIN (-2147483647L-1L)
long int _strtol_internal (const char *nptr, char **endptr, int base, int group)
{
unsigned long int result = 0;
long int sign = 1;
while (*nptr == ' ' || *nptr == '/t')
++nptr;
if (*nptr == '-')
{
sign = -1;
++nptr;
}
else if (*nptr == '+')
++nptr;
if (*nptr < '0' || *nptr > '9')
{
if (endptr != NULL)
*endptr = (char *) nptr;
return 0L;
}
assert (base == 0);
base = 10;
if (*nptr == '0')
{
if (nptr[1] == 'x' || nptr[1] == 'X')
{
base = 16;
nptr += 2;
}
else
base = 8;
}
while (*nptr >= '0' && *nptr <= '9')
{
unsigned long int digval = *nptr - '0';
if (result > LONG_MAX / 10
|| (sign > 0 ? result == LONG_MAX / 10 && digval > LONG_MAX % 10
: (result == ((unsigned long int) LONG_MAX + 1) / 10
&& digval > ((unsigned long int) LONG_MAX + 1) % 10)))
{
errno = ERANGE;
return sign > 0 ? LONG_MAX : LONG_MIN;
}
result *= base;
result += digval;
++nptr;
}
return (long int) result * sign;
}

3.strcpy
char* strcpy(char* dst,const char* src)
{
char* strDst = dst;
assert(src != NULL && dst != NULL);//拷贝空串被认为是没有意义的,使用assert检查
while ((*dst++ = *src++) != '/0');
return strDst;
}

4.memcpy
void* memcpy(void* dst,const void* src,size_t count)
{
char* pbTo = (char*)dst;
char* pbFrom = (char*)src;
assert(dst != NULL && src != NULL);
assert(pbTo >= pbFrom+count || pbFrom >= pbTo + count);//防止内存重叠(overlap)
while (count-- > 0)
{
*pbTo++ = *pbFrom++;
}
return dst;
}

5.memmove
memmove和memcpy的区别是考虑的地址重叠情况
void* memmove(void* dst,const void* src,size_t count)
{
char* pbTo = (char*)dst;
char* pbFrom = (char*)src;
assert(dst != NULL && src != NULL);
if (dst <= src || pbTo >= pbFrom + count)//没有overlap的情况,直接拷贝
{
while (count-- > 0)
{
*pbTo++ = *pbFrom++;
}
}
else
{
pbTo = pbTo + count -1;//overlap的情况,从高位地址向低位拷贝
pbFrom = pbFrom + count -1;
while (count-- > 0)
{
*pbTo-- = *pbFrom--;
}
}
return dst;
}

6.memset
void* memset(void* buf,int c,size_t count)
{
char* pvTo = (char*)buf;
assert(buf != NULL);
while (count-- >0)
{
*pvTo++ = (char)c;
}
return buf;
}

7.strcmp
int strcmp(const char* src,const char* dst)
{
int ret = 0;
if (src == dst)
{
return 0;
}
assert(NULL != src);//期待源字符串不为空
if (dst == NULL)
{
return -1;
}
while (!(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)
{
++src,++dst;
}
if (ret < 0)
{
ret = -1;
}
else if (ret > 0)
{
ret = 1;
}
return ret;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: