- 在程序设计中,被处理数据对象的数据元素之间通常有不同的逻辑关系。
- 集合是具有某种相同属性数据元素的整体。
- 由不同类型的数据元素组成的数据类型称为结构。
5.1 位运算
1. 按位与运算
左右操作数对应的每一位分别做逻辑与运算
若有语句
cout<<"10&29="<<(10&29)<<endl;
则显示结果为
2. 按位或运算
左右操作数对应的每一位分别做逻辑或运算
若有语句
cout<<"10|29="<<(10|29)<<endl;
则显示结果为
3. 按位异或运算
当左右操作数对应位不相同,即当且仅当其中一个为1时,位操作的结果才为1。
若有语句
cout<<"10|29="<<(10|29)<<endl;
则显示结果为
4. 左移
按右操作数指定位数,将左操作数按位向左移动,腾空数位补0
对于一个整数,每左移一位就相当于乘以2(结果不溢出时)
若有语句
cout<<"10<<2="<<(10<<2)<<endl;
则显示结果为
5. 右移
按右操作数指定位数,将左操作数按位向右移动
对于一个整数,每右移一位就相当于整除以2
若有语句
cout<<"12>>2="<<(12>>2)<<endl;
则显示结果为
做算术右移时,不会移动符号位
若有语句
cout<<“-12>>2="<<(-12>>2)<<endl;
则显示结果为
6. 按位取反
单目运算。对操作数按位做逻辑非.
负数在计算机中用补码表示。11110101是-11的补码。
若有语句
cout<<"~10="<<(~10)<<endl;
则显示结果为
7. 位运算的复合赋值
位运算的5个复合赋值与其他复合赋值的操作形式一致。
8.掩码
- 当一个整数的二进制串中只有一个数位为1时,称为掩码
- 程序中通常借助掩码对数据按位进行测试
例:按二进制位串形式输出正整数的值。
```cpp
#include<iostream>
using namespace std;
void bitDisplay(unsigned value);
void main()
{ unsigned x;
cout<<"Enter an unsigned integer: ";
cin>>x;
bitDisplay(x); //调用函数,以二进制形式输出正整数
}
void bitDisplay(unsigned value)
{ unsigned c;
unsigned bitMask=1<<31; //掩码,最高位置1
cout<<value<<'=';
for(c=1;c<=32;c++)
{ cout<<(value&bitMask?'1':'0'); //输出value的最高位
value<<=1; //value左移1位
if(c%8==0) cout<<' ';
}
cout<<endl;
}
结果为:
5.2 集合
- 集合是不能精确定义的基本数学概念。一般认为,当一些事物是可以按照某种性质(属性)分辨,这种事物构成的整体就称为集合。
- 根据集合的属性,可以断定某个特定的事物是否属于这个集合。如果属于,就称它为这个集合的元素。
集合的基本运算
集合运算的实现
例:集合运算的实现
//用无符号整数 表示{1…32}的 整数集合
//setH.h
#include<iostream>
using namespace std;
unsigned putX( unsigned &S, unsigned x ); //元素x并入集合S
void setPut( unsigned &S ); //输入集合S的元素
void setDisplay( unsigned S ); //输出集合S中的全部元素
unsigned Com( unsigned A, unsigned B ); //求并集A∪ B
unsigned setInt( unsigned A, unsigned B ); //求交集A∩B
unsigned setDiff( unsigned A, unsigned B); //求差集A-B
bool Inc(unsigned A, unsigned B); //判蕴含
bool In(unsigned S, unsigned x); //判属于x∈S
bool Null( unsigned S ); //判空集
//steOperate.cpp
#include"setH.h“
void setPut(unsigned & S) //输入集合元素
{ unsigned x;
cin>>x;
while( x ) { putX( S, x ); cin>>x; }
}
void setDisplay(unsigned S) //输出集合S中的全部元素
{ unsigned c;
unsigned bitMask=1;
if( Null( S ) )
{ cout<<"{ }\n“; return ; }
cout<<"{ ";
for( c=1; c<=32; c++ )
{ if( S&bitMask ) cout<<c<<", “; bitMask<<=1; }
cout<<"\b\b }\n";
}
unsigned putX(unsigned &S, unsigned x) //元素x并入集合S
{ unsigned bitMask=1;
bitMask <<= x-1;
S |= bitMask;
return S;
}
unsigned Com( unsigned A,unsigned B ) //求并集A∪B
{ return A|B;
}
unsigned setInt( unsigned A, unsigned B ) //求交集A∩B
{ return A&B;
}
unsigned setDiff( unsigned A,unsigned B) //求差集A-B
{ return A&( ~ ( A&B ) );
}
bool Inc(unsigned A,unsigned B) //判蕴含
{ if( ( A | B ) == B ) return true;
return false;
}
bool In(unsigned S,unsigned x) //判属于x∈S
{ unsigned bitMask=1;
bitMask <<= x-1;
if( S & bitMask )
return true;
return false;
}
bool Null(unsigned S) //判空集
{ if( S )
return true;
return false;
}
//test.cpp
#include "setH.h"
int main()
{ unsigned A=0, B=0;
unsigned x;
cout<<"Input the elements of set A, 1-32, until input 0 :\n";
setPut( A );
cout<<"Input the elements of set B, 1-32, until input 0 :\n";
setPut( B );
cout<<"A = ";
setDisplay( A );
cout<<"B = ";
setDisplay( B );
cout<<"Input x: ";
cin>>x;
cout<<"Put "<<x<<" in A = ";
setDisplay( putX( A,x ) );
cout<<"A+B = ";
setDisplay( Com( A, B ) );
cout<<"A*B = ";
setDisplay( setInt( A, B ) );
cout<<"A-B = ";
setDisplay( setDiff( A, B ) );
if( Inc( A, B ) )
cout <<"A <= B\n";
else
cout <<"not A <= B\n";
cout <<"Input x: ";
cin >> x;
if( In( A, x ) )
cout <<x << " in A\n";
else
cout << x << " not in A\n";
}
给出结果:
例:用数组和位运算实现集合的基本运算。用长度为N,元素为无符号整数的数组表示全集{1,2,… ,32*N }的整数集合。
//setH.h
#include<iostream>
using namespace std;
const unsigned N=4; //数组长度
typedef unsigned setType[N]; //用数组存放长度的集合
void setPut( setType S ); //输入集合S的元素
void setDisplay( const setType S ); //输出集合S中的全部元素
void putX( setType S, unsigned x ); //元素x并入集合S
void Com( setType C, const setType A, const setType B ); //求并集C=A∪B
void setInt(setType C,const setType A,const setType B ); //求交集C=A∩B
void setDiff(setType C,const setType A,const setType B); //求差集C=A-B
bool Inc( const setType A, const setType B ); //判蕴含
bool In( const setType S, unsigned x ); //判属于x∈S
bool Null( const setType S ); //判空集,空集返回true
//steOperate.cpp
#include"setH.h"
void setPut( setType S) //输入集合元素
{ unsigned x ;
cin >> x ;
while( x )
{ putX( S, x ) ; cin >> x ; }
}
void setDisplay( const setType S ) //输出集合S中的全部元素
{ unsigned c , i ; unsigned bitMask ;
if( Null( S ) ) { cout<<"{ }\n“; return ; }
cout << "{ " ;
for( i=0; i<N; i++ ) //处理每个数组元素
{ bitMask = 1 ; //掩码,32位
for( c=1; c<=32; c++ ) //按位处理
{ if( S[i] & bitMask ) cout << i*32+c << “, ” ; //输出元素
bitMask <<= 1 ;
}
}
cout << "\b\b }\n" ; //刷除最后的逗号
}
//元素x并入集合S
void putX( setType S, unsigned x )
{ unsigned bitMask = 1 ;
bitMask <<= ( (x-1)%32 ) ;
S[(x-1)/32] |= bitMask ;
}
//求并集C=A∪B
void Com( setType C, const setType A, const setType B )
{ for( int i=0; i<N; i++ )
C[i] = A[i] | B[i] ;
}
//求交集C=A∩B
void setInt( setType C, const setType A, const setType B )
{ for( int i=0; i<N; i++ )
C[i] = A[i] & B[i] ;
}
//求差集C=A-B
void setDiff( setType C, const setType A, const setType B )
{ for( int i=0; i<N; i++ )
C[i] = A[i] & ( ~( A[i] & B[i] ) ) ;
}
bool Inc( const setType A, const setType B ) //判蕴含
{ bool t = true ;
for( int i=0; i<N; i++)
{ if( ( A[i] | B[i] ) != B[i] ) t = false ; }
return t ;
}
bool In( const setType S, unsigned x ) //判属于x∈S
{ unsigned bitMask = 1 ;
bitMask <<= ( (x-1)%32 ) ;
if ( S[(x-1)/32] & bitMask ) return true;
return false;
}
bool Null( const setType S ) //判空集
{ bool t = true ;
for( int i=0; i<N; i++)
{ if( S[i] ) t = false ; }
return t ;
}
//test.cpp
#include"setH.h"
int main()
{ setType A = { 0 } , B = { 0 } , C = { 0 } ; unsigned x;
cout << "Input the elements of set A, 1-"<<32*N<<" , until input 0 :\n" ;
setPut( A );
cout << "Input the elements of set B, 1-"<<32*N<<" , until input 0 :\n" ;
setPut( B );
cout << "A = " ; setDisplay( A ) ;
cout << "B = " ; setDisplay( B );
cout << "Input x: " ; cin >> x ; putX( A, x);
cout << "Put " << x << " in A = " ; setDisplay( A );
cout << "C = A+B = " ; Com( C, A, B ); setDisplay( C );
cout << "C = A*B = " ; setInt( C, A, B ); setDisplay( C );
cout << "C = A-B = " ; setDiff( C, A, B ); setDisplay( C );
if( Inc( A, B ) ) cout << "A <= B\n" ;
else cout << "not A <= B\n" ;
cout << "Input x: " ; cin >> x ;
if( In( A, x ) ) cout << x << " in A\n" ;
else cout << x << " not in A\n" ;
}
5.3 结构
5.3.1 定义结构
结构类型定义形式为:
struct 标识符 {
类型 成员1 ;
类型 成员2 ;
…
类型 成员n ; } ;
例:
struct employee
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 20 ] ;
} ;
- 可以用不同方法定义一个结构变量
(1) 声明类型之后声明变量
例:
struct employee
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 20 ] ;
} ;
employee worker1, worker2, *Emp ;
(2) 声明类型的同时声明变量
例:
struct employee
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 20 ] ;
}worker1, worker2, *Emp ;
(3) 直接声明结构类型变量
例:
struct //注意此时没有了结构类型标识符
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 20 ] ;
} worker1, worker2, *Emp ;
- 说明
(1) 结构变量占有一片连续内存空间,具有结构类型的特征
例:
struct employee
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 20 ] ;
} ;
employee worker1, worker2, *Emp = &worker1 ;
(2) 一个结构类型的成员可以是另一个已定义的结构类型
例如: 为职工结构添加出生日期信息类型和变量声明为:
struct date
{ int month ;
int day ;
int year ;
} ;
struct employee
{ char name [ 10 ] ;
date birthday ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 11 ] ;
} worker1, worker2 ;
不能出现递归操作。
struct person
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 11 ] ;
person son;//这样是错误的。
} worker1, worker2 ;
(3) 声明结构类型变量可以同时初始化
struct employee
{ char name [ 10 ] ;
long code ;
double salary ;
char address [ 50 ] ;
char phone [ 11 ] ;
} worker = {"Wang Li " , 991083456, 1200.5, "guang zhou " , " 87111111 " } ;
5.3.2 访问结构
(1)访问结构变量的成员 结构变量 . 成员
# include <iostream>
using namespace std ;
struct weather // 声明结构类型
{ double temp;
double wind;
} ;
int main( )
{
weather today ; // 声明结构类型变量
today.temp = 10.5 ; // 对结构变量成员赋值
today.wind = 3.1 ;
cout << “Temp = ” << today.temp << endl ; // 按成员输出
cout << “Wind = ” << today.wind << endl ;
}
(2)用指针访问结构变量的成员
- 结构指针 -> 成员
- (*结构指针 ) . 成员
# include <iostream>
using namespace std ;
# include <cstring>
struct person
{ char name[20] ;
unsigned long id;
double salary;
};
int main( )
{ person pr1 ;
person *pp ; // 定义结构指针
pp = & pr1 ; // 取结构变量地址
strcpy ( pp -> name , “David Marat” ) ; // 对结构成员赋值
pp -> id = 987654321 ;// (*pp). id
pp -> salary = 335.0 ;(*pp). salary
cout << pp -> name << ‘\t’ << pp -> id << ‘\t’ << pp -> salary << endl ;
}
(3)类型相同的结构变量可以整体赋值
# include <iostream>
using namespace std ;
struct weather
{ double temp;
double wind;
} yesterday ;
int main ( )
{ weather today ;
yesterday . temp = 10.5 ;
yesterday . wind = 3.1 ;
today = yesterday ; // 结构变量整体赋值
cout << “Temp = ” << today . temp << endl ;
cout << “Wind = ” << today . wind << endl ;
}
“类型相同的变量” 是指用同一类型标识符说明的变量
例如:
struct weather1
{ double temp;
double wind;
} yesterday ;
struct weather2
{ double temp;
double wind;
} today ;
yesterday 和 today尽管成员相同,但不是同类型变量不可以整体赋值。
5.3.3 结构参数
函数的结构类型参数可以传值、指针及引用参数
例:
- 传值得方式。
# include <iostream>
using namespace std ;
struct weather
{ double temp;
double wind;
} ;
void funstu(weather w)
{
w.temp=15;
w.wind=2.3;
cout << " fun:\nTemp = " << w.temp << endl ;
cout << "Wind = " << w.wind << endl ;
}
int main()
{
weather day ;
day.temp = 10.5 ;
day.wind = 3.1 ;
cout << " 1:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
funstu(day);
cout << " 2:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
}
- 传引用参数
# include <iostream>
using namespace std ;
struct weather
{ double temp;
double wind;
} ;
void funstu(weather &w)
{
w.temp=15;
w.wind=2.3;
cout << " fun:\nTemp = " << w.temp << endl ;
cout << "Wind = " << w.wind << endl ;
}
int main()
{ weather day ;
day . temp = 10.5 ;
day . wind = 3.1 ;
cout << " 1:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
funstu(day);
cout << " 2:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
}
- 传地址的方式
# include <iostream>
using namespace std ;
struct weather
{ double temp;
double wind;
} ;
void funstu(weather *w)
{
w->temp=15;
w->wind=2.3;
cout << " fun:\nTemp = " << w->temp << endl ;
cout << "Wind = " << w->wind << endl ;
}
int main()
{
weather day ;
day . temp = 10.5 ;
day . wind = 3.1 ;
cout << " 1:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
funstu(&day);
cout << " 2:\nTemp = " << day.temp << endl ;
cout << "Wind = " << day.wind << endl ;
}
结果为:
5.4 结构数组
数组的元素类型为结构类型时,称为结构数组。
例如
struct S_type
{
int a;
double x;
};
S_type S_ary[10];
S_ary是一个有10个元素的数组,元素类型是S_type。
数组的每一个元素包含两个数据成员。
S_ary[0].a S_ary[0].x
S_ary[1].a S_ary[1].x
……
S_ary[9].a S_ary[9].x
例:对结构数组以某一成员作关键字排序
# include <iostream>
using namespace std ;
struct person // 结构定义
{ char name[10] ;
unsigned int id;
double salary ;
} ;
person allone[6] ; // 结构数组声明
int main()
{ int i ;
person temp ; // 结构变量声明
for ( i = 0 ; i < 6 ; i ++ ) // 输入数据
{
cout << i << ": name: " ; cgets ( allone[i].name ) ;//cgets接收空格输入
cout << "id: " ; cin >> allone[i].id ;
cout << "salary: " ; cin >> allone[i].salary ;
cout << endl ;
} ;
cout << "Sort:\n" ;
for ( i=1 ; i < 6 ; i ++ ) // 以成员salary作关键字排序
{ for ( int j = 0 ; j <= 5-i ; j ++ )
{
if ( allone[j].salary > allone[j+1].salary ) // 结构变量的整体交换
{
temp = allone[j] ;
allone[j] = allone[j+1] ;
allone[j+1] = temp ;
}
}
}
for ( i = 0 ; i < 6 ; i ++ ) // 输出排序后数据
cout << allone[i].name << '\t' << allone[i].id << '\t' << allone[i].salary << endl ;
}
实际上,这样做会产生问题。
问题: 结构变量的整体交换降低了排序效率
解决: 使用索引机制, 建立结构指针数组
方法 :
-
- 建立索引数组
-
- 以关键字作依据进行数据比较,移动索引
-
- 通过索引访问数据
使用索引排序
- 建立索引数组
- 排序(冒泡法)
pa [ 0 ]->salary < pa [ 1 ] -> salary
即 allone[0] . salary < allone[1] . salary
不交换
pa [ 1 ]->salary > pa [ 2 ] ->salary
即 allone[1] . salary > allone[2] . salary
pa [ 1 ] 与 pa [ 2 ]交换
一次类推。
最后输出排序结果
:
person * pa[6] = { &allone[0] , &allone[1] , &allone[2] ,
&allone[3] , &allone[4] , &allone[5] };
person * temp;
:
for ( i = 1 ; i < 6 ; i + + )
{ for ( int j = 0 ; j <= 5 - i ; j + + )
{ if ( pa [ j ] -> salary > pa [ j+1 ] -> salary )
{ temp = pa [ j ] ; pa [ j ] = pa [ j+1 ] ; pa [ j+1 ] = temp ;
}
}
}
5.5 链表
1.动态链表存储
结点数据类型
struct node
{
char name[20] ;
double salary ;
node * next ;
} ;
单向链表结点数据类型
struct node
{
dataType data ;
node * next ;
} ;
2.建立和遍历链表
建立链表的过程:
生成头结点;
while(未结束)
{
生成新结点;
把新结点插入链表;
}
struct node
{
int data ;
node * next ;
} ;
node * head ;//头指针
node * CreateList()
{
node * s, * p ; //声明局部量
s = new node ;//建立第一个结点
cin >> s->data ;
head = NULL ;//链表头指针初始化
while ( s->data != 0 )//插入新结点
{
if ( head == NULL ) head = s ;
else p->next = s ;
p = s ;
s = new node ;
cin >> s->data ;
}
p -> next = NULL ;
delete s ;//释放值为0的结点
return ( head ) ;
}
3.插入结点
- 在表头插入结点
s -> next = head ;
head = s ;
- 在p之后插入s
s -> next = p -> next ;
p -> next = s ;
- 在p之前插入s
方法一 知道前驱结点
s -> next = p ;
q -> next = s ;
方法二 尾插交换数据
s -> next = p -> next ;
p -> next = s ;
t = p -> data ;
p -> data = s-> data;
s-> data = t ;
例:用插入法生成一个有序链表
算法思路:
if (表空) 生成链表的第一个结点;
else if ( num < head->data )
把 num 插入头结点之前; //此时num是最小值
else
{ if (找到) 找第一个大于num的结点*p;
把num插入*p之前;
}
else 把num插入表尾; //此时num是最大值
#include<iostream>
using namespace std ;
struct list
{ int data ;
list * next ;
} ;
list * head ;
list * insert(int num )
{ list * s, *p, *q ;
s = new list ;
s->data = num ; s->next = NULL ;
if ( head == NULL )
{ head = s ; return( head ) ; }
if ( head->data > s->data )
{ s->next = head ; head = s ;
return ( head ) ;
}
for ( q = head, p = head->next ; p ; q = p, p = p->next )
if ( p->data > s->data )
{ s->next = p ; q->next = s ;
return ( head ) ;
}
q->next = s ;
return ( head ) ;
}
void showlist( const list * head )
{ cout << "now the items of list are: \n" ;
while( head )
{ cout << head->data << '\t'; head = head->next ; }
cout << endl ;
}
int main()
{ int k ;
head = NULL ;
cin >> k ;
while( k != 0 )
{ head = insert(k) ; cin >> k ; }
showlist( head ) ;
}
4.删除结点
- 删除头结点
p = head ;
head = head->next ;
delete p ;
- 删除结点 *p
q->next = p->next ;
delete p ;
这里给出一个著名问题:约瑟夫(Jonsephus)问题
n个人围成一个环,从第i个开始,由1至interval不断报数,凡报到interval的出列,直到环空为止。
例:
n=8,i=1,interval=3,则输出序列为:
3 6 1 5 2 8 4 7
对数据操作
找开始报数位置 ;
while ( 表结点数 >1 )
{ for ( 1 To interval )
{ 结点计数;
输出,删除第 interval 个结点 ;
}
}
输出,删除最后一个结点 ;
// 例 约瑟夫问题
#include <iostream>
#include <iomanip>
using namespace std ;
struct Jonse { int code ; Jonse *next ; } ;//声明结构类型
Jonse * Create( int ) ;
void ShowList( Jonse * ) ;
void Out( Jonse *, int, int ) ;
int main()
{ Jonse * head ;
int num , val , beg ;
cout << "\nplease input the number of total:\n" ;
cin >> num ; //输入人数
head = Create(num) ;
ShowList(head) ;
cout << "\nplease input the code of begin:\n" ;
cin >> beg ; //输入开始报数的位置
cout << "\nplease input interval of counting:\n" ;
cin >> val ; //输入报数间隔
cout << "the new list is:\n" ;
Out( head, beg, val ) ; 以约瑟夫方式输出链表
}
Jonse * Create ( int n )//创建n个结点的单向链表
{
Jonse *h, *p ;
h = new Jonse ;建立第一个结点
p = h ;
for ( int i = 1 ; i<=n ; i++ )//建立后续结点
{ p->code = i ; //向结点赋序号
if (i<n){ p->next = new Jonse ; p = p->next ; }//在表尾建立新结点
}
p->next = h ; //构成链环
return h ;
}
void ShowList(Jonse *h)
{ Jonse *p ; p = h ;
do { cout << p->code << '\t' ; p = p->next ;
} while ( p!=h ) ;
}
void Out(Jonse *h, int i, int d)
{ Jonse *p,*q ; //建立双跟踪指针
int k ; p = h ; //从表头开始操作
for( q = h ; q->next != h ; q = q->next ) ; //寻找p的前驱q
for( k = 1; k<i; k++) { q = p ; p = p->next ; }//寻找开始位置
while( p != p->next )//处理链环直至剩下一个结点
{ for ( k = 1 ; k<d ; k++ ) { q = p ; p = p->next ; }//报数
cout << p->code << '\t' ; //输出报到结点
q->next = p->next ;//删除结点
delete p ;
p = q->next ;//移动指针
}
cout << p->code << endl ;//输出、删除最后一个结点
delete p ;
}